
/*-------------------------------------------------------------------------
 *
 * numeric.c
 *	  Postgres 数据库系统的精确数字数据类型
 *
 * 原始编码 1998, Jan Wieck.  2003年大幅修改，Tom Lane。
 *
 * 很多算法思想借鉴了 David M. Smith 的 "FM"
 * 多精度数学库，最近发布的算法为
 * 786：多精度复数算术与函数，ACM
 * 数学软件交易，卷 24，第 4 期，1998年12月，
 * 第359-367页。
 *
 * Copyright (c) 1998-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/numeric.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <math.h>

#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "common/int.h"
#include "funcapi.h"
#include "lib/hyperloglog.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/numeric.h"
#include "utils/pg_lsn.h"
#include "utils/sortsupport.h"

/* ----------
 * 取消注释以启用 dump_numeric() 和 dump_var() 的编译，
 * 并获取 make_result() 生成的任何结果的转储。
 * ----------
#define NUMERIC_DEBUG
 */


/* ----------
 * 本地数据类型
 *
 * 数值以基数为 NBASE 的浮点格式表示。
 * 每个“数字”的范围是 0 到 NBASE-1。类型 NumericDigit 是有符号的
 * 并且足够宽以存储一个数字。我们假设 NBASE*NBASE 可以适合
 * 一个整数。尽管纯计算程序可以处理任何少于 sqrt(INT_MAX) 的
 * 阶段 NBASE，但实际上我们只对 NBASE 的 10 的幂感兴趣，以便于
 * I/O 转换和十进制舍入。同时，如果 NBASE 小于 sqrt(INT_MAX)，
 * 其实更高效，这样 mul_var 和 div_var_fast 就有了
 * 推迟处理进位的“余地”。
 *
 * 除了 10000 以外的 NBASE 值被认为只有历史意义，
 * 并且不再在任何意义上得到支持；没有机制允许客户端
 * 发现基数，因此每个支持二进制模式的客户端都期望
 * 使用基于 10000 的格式。如果你计划更改此行为，还请注意数字
 * 缩写代码，该代码假设 NBASE=10000。
 * ----------
 */

#if 0
#define NBASE		10
#define HALF_NBASE	5
#define DEC_DIGITS	1			/* 每个 NBASE 数字的十进制数字 */
#define MUL_GUARD_DIGITS	4	/* 这些是以 NBASE 数字计量的 */
#define DIV_GUARD_DIGITS	8

typedef signed char NumericDigit;
#endif

#if 0
#define NBASE		100
#define HALF_NBASE	50
#define DEC_DIGITS	2			/* 每个 NBASE 数字的十进制数字 */
#define MUL_GUARD_DIGITS	3	/* 这些是以 NBASE 数字计量的 */
#define DIV_GUARD_DIGITS	6

typedef signed char NumericDigit;
#endif

#if 1
#define NBASE		10000
#define HALF_NBASE	5000
#define DEC_DIGITS	4			/* 每个 NBASE 数字的十进制数字 */
#define MUL_GUARD_DIGITS	2	/* 这些是以 NBASE 数字计量的 */
#define DIV_GUARD_DIGITS	4

typedef int16 NumericDigit;
#endif


/*
 * 数值类型在磁盘上的存储形式。
 *
 * 如果 NumericChoice 的第一个字（n_header，或 n_short.n_header，或 n_long.n_sign_dscale）的高位是 NUMERIC_SHORT，那么数值遵循 NumericShort 格式；如果它们是 NUMERIC_POS 或 NUMERIC_NEG，则遵循 NumericLong 格式。如果它们是 NUMERIC_SPECIAL，则值为 NaN 或无穷大。我们目前总是仅使用两个字节（即仅 n_header）来存储特殊值，但以前的版本仅使用 NumericLong 格式，因此如果数据库是通过 pg_upgrade 迁移的，我们可能会在磁盘上找到 4 字节的 NaNs（尽管没有无穷大）。在这两种情况下，特殊值头部的低位是保留的，目前应该始终设置为零。
 *
 * 在 NumericShort 格式中，头部字的剩余 14 位（n_short.n_header）分配如下：1 位表示符号（正或负），6 位表示动态小数位，7 位表示权重。在实践中，大多数常见的值可以通过这种方式表示。
 *
 * 在 NumericLong 格式中，头部字的剩余 14 位（n_long.n_sign_dscale）表示显示小数位；权重则单独存储在 n_weight 中。
 *
 * 注意：根据约定，打包形式的值已去掉所有前导和尾随零数字（其中数字的基数为 NBASE）。特别是，如果值为零，则根本没有数字！在这种情况下，权重是任意的，但我们通常将其设置为零。
 */

struct NumericShort
{
	uint16		n_header;		/* 符号 + 显示小数位 + 权重 */
	NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */
};

struct NumericLong
{
	uint16		n_sign_dscale;	/* 符号 + 显示小数位 */
	int16		n_weight;		/* 第一位数字的权重 */
	NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */
};

union NumericChoice
{
	uint16		n_header;		/* 头部字 */
	struct NumericLong n_long;	/* 长格式（4 字节头部） */
	struct NumericShort n_short;	/* 短格式（2 字节头部） */
};

struct NumericData
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	union NumericChoice choice; /* 格式选择 */
};


/*
 * 高位的解释。
 */

#define NUMERIC_SIGN_MASK	0xC000
#define NUMERIC_POS			0x0000
#define NUMERIC_NEG			0x4000
#define NUMERIC_SHORT		0x8000
#define NUMERIC_SPECIAL		0xC000

#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK)
#define NUMERIC_IS_SHORT(n)		(NUMERIC_FLAGBITS(n) == NUMERIC_SHORT)
#define NUMERIC_IS_SPECIAL(n)	(NUMERIC_FLAGBITS(n) == NUMERIC_SPECIAL)

#define NUMERIC_HDRSZ	(VARHDRSZ + sizeof(uint16) + sizeof(int16))
#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16))

/*
 * 如果标志位是 NUMERIC_SHORT 或 NUMERIC_SPECIAL，我们想要短头部；否则，我们想要长头部。为了稍微提高效率，而不是针对每个值进行测试，我们可以直接查看高位。
 */
#define NUMERIC_HEADER_IS_SHORT(n)	(((n)->choice.n_header & 0x8000) != 0)
#define NUMERIC_HEADER_SIZE(n) \
	(VARHDRSZ + sizeof(uint16) + \
	 (NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16)))

/*
 * 特殊值的定义（NaN，正无穷大，负无穷大）。
 *
 * NUMERIC_SPECIAL 位后的两位为 00 表示 NaN， 01 表示正无穷大， 11 表示负无穷大。 （这使得符号位与短格式值中的位置匹配，尽管我们目前没有使用这一点。）我们可以在测试活动位之前屏蔽掉其余位，但目前这些位必须为零，因此屏蔽仅会增加周期。
 */
#define NUMERIC_EXT_SIGN_MASK	0xF000	/* 高位加 NaN/无穷大标志位 */
#define NUMERIC_NAN				0xC000
#define NUMERIC_PINF			0xD000
#define NUMERIC_NINF			0xF000
#define NUMERIC_INF_SIGN_MASK	0x2000

#define NUMERIC_EXT_FLAGBITS(n)	((n)->choice.n_header & NUMERIC_EXT_SIGN_MASK)
#define NUMERIC_IS_NAN(n)		((n)->choice.n_header == NUMERIC_NAN)
#define NUMERIC_IS_PINF(n)		((n)->choice.n_header == NUMERIC_PINF)
#define NUMERIC_IS_NINF(n)		((n)->choice.n_header == NUMERIC_NINF)
#define NUMERIC_IS_INF(n) \
	(((n)->choice.n_header & ~NUMERIC_INF_SIGN_MASK) == NUMERIC_PINF)

/*
 * 简短格式定义。
 */

#define NUMERIC_SHORT_SIGN_MASK			0x2000
#define NUMERIC_SHORT_DSCALE_MASK		0x1F80
#define NUMERIC_SHORT_DSCALE_SHIFT		7
#define NUMERIC_SHORT_DSCALE_MAX		\
	(NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT)
#define NUMERIC_SHORT_WEIGHT_SIGN_MASK	0x0040
#define NUMERIC_SHORT_WEIGHT_MASK		0x003F
#define NUMERIC_SHORT_WEIGHT_MAX		NUMERIC_SHORT_WEIGHT_MASK
#define NUMERIC_SHORT_WEIGHT_MIN		(-(NUMERIC_SHORT_WEIGHT_MASK+1))

/*
 * 提取符号，显示比例，权重。这些宏从Numeric（磁盘上的格式）提取适合NumericVar格式的字段值。
 *
 * 请注意，我们并未确保对于无穷大，dscale和weight读取为零；然而，这并不重要，因为我们从未将“特殊”数字转换为NumericVar形式。只有下面定义的常量（const_nan等）才以NumericVar表示非有限值。
 */

#define NUMERIC_DSCALE_MASK			0x3FFF
#define NUMERIC_DSCALE_MAX			NUMERIC_DSCALE_MASK

#define NUMERIC_SIGN(n) \
	(NUMERIC_IS_SHORT(n) ? \
		(((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \
		 NUMERIC_NEG : NUMERIC_POS) : \
		(NUMERIC_IS_SPECIAL(n) ? \
		 NUMERIC_EXT_FLAGBITS(n) : NUMERIC_FLAGBITS(n)))
#define NUMERIC_DSCALE(n)	(NUMERIC_HEADER_IS_SHORT((n)) ? \
	((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \
		>> NUMERIC_SHORT_DSCALE_SHIFT \
	: ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK))
#define NUMERIC_WEIGHT(n)	(NUMERIC_HEADER_IS_SHORT((n)) ? \
	(((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \
		~NUMERIC_SHORT_WEIGHT_MASK : 0) \
	 | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
	: ((n)->choice.n_long.n_weight))

/*
 * 存储的Numeric值的最大权重（基于在NumericLong中使用int16作为权重）。请注意，存储在NumericVar和NumericSumAccum变量中的中间值可能具有更大的权重。
 */
#define NUMERIC_WEIGHT_MAX			PG_INT16_MAX

/* ----------
 * NumericVar是我们用于算术计算的格式。数字数组部分与NumericData存储格式相同，但头部更复杂。
 *
 * NumericVar表示的值由符号、权重、ndigits和digits[]数组决定。如果它是“特殊”值（NaN或Inf），那么只有符号字段重要；ndigits应该为零，权重和dscale字段被忽略。
 *
 * 注意：NumericVar值的第一个数字假定被乘以NBASE ** 权重。换句话说，在小数点前有weight+1个数字。权重可能小于0。
 *
 * buf指向为NumericVar分配的数字缓冲区的物理起始位置。digits指向实际使用中的第一个数字（具有指定权重的数字）。我们通常在buf和digits之间留有一个或两个未使用的数字（预设为零），以便可以在不重新分配空间的情况下存储从最高位数字溢出的进位。我们只需要递减digits（并增加weight）以为进位数字腾出空间。（在数据库中存储的数字值中没有这种额外空间，只有在内存中的NumericVar中才有。）
 *
 * 如果buf为NULL，则数字缓冲区实际上并未分配，因此不应释放——请参见下面的常量示例。
 *
 * dscale，或显示比例，是表示为小数点后位数的名义精度（目前必须始终>= 0）。dscale可能大于物理存储的分数字符数，暗示我们压制了重要的尾随零的存储。它绝不应少于存储的数字数，因为这将暗示隐藏存在的数字。注意，dscale始终以*十进制*数字表示，因此它可能对应于NBASE数字的分数数量——除以DEC_DIGITS以转换为NBASE数字。
 *
 * rscale，或结果比例，是计算的目标精度。与dscale类似，它以小数点后的*十进制*数字数量表示，并且目前始终>= 0。
 * 注意，rscale不存储在变量中——它是从输入的dscale动态计算得出的。
 *
 * 虽然我们始终使用“权重”来指代数字值的基数-NBASE权重，但在某些与比例相关的计算中，利用基数-10的权重（即值的近似log10）是方便的。为了避免混淆，这种十进制单位的权重称为“dweight”。
 *
 * 注意：所有变量级函数的写法使得可以将同一个变量作为参数和目标。这是可行的，因为数字缓冲区与变量是分开的。
 * ----------
 */
typedef struct NumericVar
{
	int			ndigits;		/* digits[]中的数字个数 - 可以是0! */
	int			weight;			/* 第一位数字的权重 */
	int			sign;			/* NUMERIC_POS, _NEG, _NAN, _PINF, 或 _NINF */
	int			dscale;			/* 显示刻度 */
	NumericDigit *buf;			/* palloc'd空间的起始位置 */
	NumericDigit *digits;		/* 基于NBASE的数字 */
} NumericVar;


/* ----------
 * generate_series的数据
 * ----------
 */
typedef struct
{
	NumericVar	current;
	NumericVar	stop;
	NumericVar	step;
} generate_series_numeric_fctx;


/* ----------
 * 排序支持。
 * ----------
 */
typedef struct
{
	void	   *buf;			/* 短varlenas的缓冲区 */
	int64		input_count;	/* 看到的非空值数量 */
	bool		estimating;		/* 如果估计基数为真 */

	hyperLogLogState abbr_card; /* 基数估算器 */
} NumericSortSupport;


/* ----------
 * 快速和累加器。
 *
 * NumericSumAccum 用于实现 SUM() 和其他标准聚合
 * ，用于跟踪输入值的总和。它使用 32 位整数存储
 * 数字，而不是普通的 16 位整数（NBASE=10000）。
 * 这样，我们可以安全地累加最多 NBASE - 1 个值而不
 * 传播进位，在此之前不会冒任何数字溢出的风险。
 * 'num_uncarried' 跟踪在不传播进位的情况下累加了多少值。
 *
 * 正数和负数值分别在 'pos_digits'
 * 和 'neg_digits' 中累加。这比对每个新值决定
 * 是加还是减当前值要简单和快速得多（请查看 sub_var()
 * 了解我们通过这样做避免的逻辑）。两个缓冲区的大小相同，
 * 权重和规模也相同。在 accum_sum_final() 中，正数和
 * 负数总和被相加以生成最终结果。
 *
 * 当新值的 ndigits 或权重大于当前累加器
 * 所具有的时，累加器会扩大以容纳新值。
 * 我们通常保留一个零数字用于进位传播，这通过
 * 'have_carry_space' 标志指示。当 accum_sum_carry()
 * 使用了保留的数字时，它会清除 'have_carry_space' 标志。
 * 下一次调用 accum_sum_add() 时会扩大缓冲区，以
 * 为额外的数字腾出空间，并再次设置标志。
 *
 * 要初始化新的累加器，只需将所有字段重置为零。
 *
 * 累加器不处理 NaN。
 * ----------
 */
typedef struct NumericSumAccum
{
	int			ndigits;
	int			weight;
	int			dscale;
	int			num_uncarried;
	bool		have_carry_space;
	int32	   *pos_digits;
	int32	   *neg_digits;
} NumericSumAccum;


/*
 * 我们定义自己的宏来打包和解包数值的缩写键
 * 表示以避免依赖于 USE_FLOAT8_BYVAL。
 * 我们使用的缩写类型仅基于
 * 数据的大小，而不是 float8 的参数传递约定。
 *
 * 有穷值的缩写范围从 +PG_INT64/32_MAX
 * 到 -PG_INT64/32_MAX。NaN 的缩写为 PG_INT64/32_MIN，
 * 我们定义排序顺序使其正常工作（请参阅下面更多
 * 注释）。PINF 和 NINF 共享最大的
 * 和最小的有穷缩写类的缩写。
 */
#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE)
#if SIZEOF_DATUM == 8
#define NumericAbbrevGetDatum(X) ((Datum) (X))
#define DatumGetNumericAbbrev(X) ((int64) (X))
#define NUMERIC_ABBREV_NAN		 NumericAbbrevGetDatum(PG_INT64_MIN)
#define NUMERIC_ABBREV_PINF		 NumericAbbrevGetDatum(-PG_INT64_MAX)
#define NUMERIC_ABBREV_NINF		 NumericAbbrevGetDatum(PG_INT64_MAX)
#else
#define NumericAbbrevGetDatum(X) ((Datum) (X))
#define DatumGetNumericAbbrev(X) ((int32) (X))
#define NUMERIC_ABBREV_NAN		 NumericAbbrevGetDatum(PG_INT32_MIN)
#define NUMERIC_ABBREV_PINF		 NumericAbbrevGetDatum(-PG_INT32_MAX)
#define NUMERIC_ABBREV_NINF		 NumericAbbrevGetDatum(PG_INT32_MAX)
#endif


/* ----------
 * 一些预初始化常量
 * ----------
 */
static const NumericDigit const_zero_data[1] = {0};
static const NumericVar const_zero =
{0, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_zero_data};

static const NumericDigit const_one_data[1] = {1};
static const NumericVar const_one =
{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_one_data};

static const NumericVar const_minus_one =
{1, 0, NUMERIC_NEG, 0, NULL, (NumericDigit *) const_one_data};

static const NumericDigit const_two_data[1] = {2};
static const NumericVar const_two =
{1, 0, NUMERIC_POS, 0, NULL, (NumericDigit *) const_two_data};

#if DEC_DIGITS == 4
static const NumericDigit const_zero_point_nine_data[1] = {9000};
#elif DEC_DIGITS == 2
static const NumericDigit const_zero_point_nine_data[1] = {90};
#elif DEC_DIGITS == 1
static const NumericDigit const_zero_point_nine_data[1] = {9};
#endif
static const NumericVar const_zero_point_nine =
{1, -1, NUMERIC_POS, 1, NULL, (NumericDigit *) const_zero_point_nine_data};

#if DEC_DIGITS == 4
static const NumericDigit const_one_point_one_data[2] = {1, 1000};
#elif DEC_DIGITS == 2
static const NumericDigit const_one_point_one_data[2] = {1, 10};
#elif DEC_DIGITS == 1
static const NumericDigit const_one_point_one_data[2] = {1, 1};
#endif
static const NumericVar const_one_point_one =
{2, 0, NUMERIC_POS, 1, NULL, (NumericDigit *) const_one_point_one_data};

static const NumericVar const_nan =
{0, 0, NUMERIC_NAN, 0, NULL, NULL};

static const NumericVar const_pinf =
{0, 0, NUMERIC_PINF, 0, NULL, NULL};

static const NumericVar const_ninf =
{0, 0, NUMERIC_NINF, 0, NULL, NULL};

#if DEC_DIGITS == 4
static const int round_powers[4] = {0, 1000, 100, 10};
#endif



/**********
 * 本地函数
 * **********/

#ifdef NUMERIC_DEBUG
static void fc_dump_numeric(const char *fc_str, Numeric fc_num);
static void fc_dump_var(const char *fc_str, NumericVar *fc_var);
#else
#define fc_dump_numeric(s,n)
#define fc_dump_var(s,v)
#endif

#define digitbuf_alloc(ndigits)  \
	((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit)))
#define digitbuf_free(buf)	\
	do { \
		 if ((buf) != NULL) \
			 pfree(buf); \
	} while (0)

#define init_var(v)		memset(v, 0, sizeof(NumericVar))

#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \
	(num)->choice.n_short.n_data : (num)->choice.n_long.n_data)
#define NUMERIC_NDIGITS(num) \
	((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit))
#define NUMERIC_CAN_BE_SHORT(scale,weight) \
	((scale) <= NUMERIC_SHORT_DSCALE_MAX && \
	(weight) <= NUMERIC_SHORT_WEIGHT_MAX && \
	(weight) >= NUMERIC_SHORT_WEIGHT_MIN)

static void fc_alloc_var(NumericVar *fc_var, int fc_ndigits);
static void fc_free_var(NumericVar *fc_var);
static void fc_zero_var(NumericVar *fc_var);

static const char *set_var_from_str(const char *fc_str, const char *fc_cp,
									NumericVar *fc_dest);
static void set_var_from_num(Numeric fc_value, NumericVar *fc_dest);
static void fc_init_var_from_num(Numeric fc_num, NumericVar *fc_dest);
static void set_var_from_var(const NumericVar *fc_value, NumericVar *fc_dest);
static char *get_str_from_var(const NumericVar *fc_var);
static char *get_str_from_var_sci(const NumericVar *fc_var, int fc_rscale);

static void fc_numericvar_serialize(StringInfo fc_buf, const NumericVar *fc_var);
static void fc_numericvar_deserialize(StringInfo fc_buf, NumericVar *fc_var);

static Numeric fc_duplicate_numeric(Numeric fc_num);
static Numeric fc_make_result(const NumericVar *fc_var);
static Numeric fc_make_result_opt_error(const NumericVar *fc_var, bool *fc_error);

static void fc_apply_typmod(NumericVar *fc_var, int32 fc_typmod);
static void fc_apply_typmod_special(Numeric fc_num, int32 fc_typmod);

static bool fc_numericvar_to_int32(const NumericVar *fc_var, int32 *fc_result);
static bool fc_numericvar_to_int64(const NumericVar *fc_var, int64 *fc_result);
static void fc_int64_to_numericvar(int64 fc_val, NumericVar *fc_var);
static bool fc_numericvar_to_uint64(const NumericVar *fc_var, uint64 *fc_result);
#ifdef HAVE_INT128
static bool fc_numericvar_to_int128(const NumericVar *fc_var, int128 *fc_result);
static void fc_int128_to_numericvar(int128 fc_val, NumericVar *fc_var);
#endif
static double fc_numericvar_to_double_no_overflow(const NumericVar *fc_var);

static Datum fc_numeric_abbrev_convert(Datum fc_original_datum, SortSupport fc_ssup);
static bool fc_numeric_abbrev_abort(int fc_memtupcount, SortSupport fc_ssup);
static int	fc_numeric_fast_cmp(Datum fc_x, Datum fc_y, SortSupport fc_ssup);
static int	fc_numeric_cmp_abbrev(Datum fc_x, Datum fc_y, SortSupport fc_ssup);

static Datum fc_numeric_abbrev_convert_var(const NumericVar *fc_var,
										NumericSortSupport *fc_nss);

static int	fc_cmp_numerics(Numeric fc_num1, Numeric fc_num2);
static int	fc_cmp_var(const NumericVar *fc_var1, const NumericVar *fc_var2);
static int	fc_cmp_var_common(const NumericDigit *fc_var1digits, int fc_var1ndigits,
						   int fc_var1weight, int fc_var1sign,
						   const NumericDigit *fc_var2digits, int fc_var2ndigits,
						   int fc_var2weight, int fc_var2sign);
static void add_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_sub_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_mul_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result,
					int fc_rscale);
static void fc_div_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result,
					int fc_rscale, bool fc_round);
static void fc_div_var_fast(const NumericVar *fc_var1, const NumericVar *fc_var2,
						 NumericVar *fc_result, int fc_rscale, bool fc_round);
static void fc_div_var_int(const NumericVar *fc_var, int fc_ival, int fc_ival_weight,
						NumericVar *fc_result, int fc_rscale, bool fc_round);
static int	fc_select_div_scale(const NumericVar *fc_var1, const NumericVar *fc_var2);
static void fc_mod_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_div_mod_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
						NumericVar *fc_quot, NumericVar *fc_rem);
static void fc_ceil_var(const NumericVar *fc_var, NumericVar *fc_result);
static void fc_floor_var(const NumericVar *fc_var, NumericVar *fc_result);

static void fc_gcd_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_sqrt_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale);
static void fc_exp_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale);
static int	fc_estimate_ln_dweight(const NumericVar *fc_var);
static void fc_ln_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale);
static void fc_log_var(const NumericVar *fc_base, const NumericVar *fc_num,
					NumericVar *fc_result);
static void fc_power_var(const NumericVar *fc_base, const NumericVar *fc_exp,
					  NumericVar *fc_result);
static void fc_power_var_int(const NumericVar *fc_base, int fc_exp, NumericVar *fc_result,
						  int fc_rscale);
static void fc_power_ten_int(int fc_exp, NumericVar *fc_result);

static int	fc_cmp_abs(const NumericVar *fc_var1, const NumericVar *fc_var2);
static int	fc_cmp_abs_common(const NumericDigit *fc_var1digits, int fc_var1ndigits,
						   int fc_var1weight,
						   const NumericDigit *fc_var2digits, int fc_var2ndigits,
						   int fc_var2weight);
static void add_abs(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_sub_abs(const NumericVar *fc_var1, const NumericVar *fc_var2,
					NumericVar *fc_result);
static void fc_round_var(NumericVar *fc_var, int fc_rscale);
static void fc_trunc_var(NumericVar *fc_var, int fc_rscale);
static void fc_strip_var(NumericVar *fc_var);
static void fc_compute_bucket(Numeric fc_operand, Numeric fc_bound1, Numeric fc_bound2,
						   const NumericVar *fc_count_var, bool fc_reversed_bounds,
						   NumericVar *fc_result_var);

static void fc_accum_sum_add(NumericSumAccum *fc_accum, const NumericVar *fc_var1);
static void fc_accum_sum_rescale(NumericSumAccum *fc_accum, const NumericVar *fc_val);
static void fc_accum_sum_carry(NumericSumAccum *fc_accum);
static void fc_accum_sum_reset(NumericSumAccum *fc_accum);
static void fc_accum_sum_final(NumericSumAccum *fc_accum, NumericVar *fc_result);
static void fc_accum_sum_copy(NumericSumAccum *fc_dst, NumericSumAccum *fc_src);
static void fc_accum_sum_combine(NumericSumAccum *fc_accum, NumericSumAccum *fc_accum2);


/* ----------------------------------------------------------------------
 *
 * 输入、输出和舍入函数
 *
 * ----------------------------------------------------------------------
 */


/*
 * numeric_in() -
 *
 *	用于数值数据类型的输入函数
 */
Datum numeric_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	Numeric		fc_res;
	const char *fc_cp;

	/* 跳过前导空格 */
	fc_cp = fc_str;
	while (*fc_cp)
	{
		if (!isspace((unsigned char) *fc_cp))
			break;
		fc_cp++;
	}

	/*
	 * 检查 NaN 和无穷大。我们识别 float8in() 允许的相同字符串。
	 */
	if (pg_strncasecmp(fc_cp, "NaN", 3) == 0)
	{
		fc_res = fc_make_result(&const_nan);
		fc_cp += 3;
	}
	else if (pg_strncasecmp(fc_cp, "Infinity", 8) == 0)
	{
		fc_res = fc_make_result(&const_pinf);
		fc_cp += 8;
	}
	else if (pg_strncasecmp(fc_cp, "+Infinity", 9) == 0)
	{
		fc_res = fc_make_result(&const_pinf);
		fc_cp += 9;
	}
	else if (pg_strncasecmp(fc_cp, "-Infinity", 9) == 0)
	{
		fc_res = fc_make_result(&const_ninf);
		fc_cp += 9;
	}
	else if (pg_strncasecmp(fc_cp, "inf", 3) == 0)
	{
		fc_res = fc_make_result(&const_pinf);
		fc_cp += 3;
	}
	else if (pg_strncasecmp(fc_cp, "+inf", 4) == 0)
	{
		fc_res = fc_make_result(&const_pinf);
		fc_cp += 4;
	}
	else if (pg_strncasecmp(fc_cp, "-inf", 4) == 0)
	{
		fc_res = fc_make_result(&const_ninf);
		fc_cp += 4;
	}
	else
	{
		/*
		 * 使用 set_var_from_str() 解析正常的数值
		 */
		NumericVar	fc_value;

		init_var(&fc_value);

		fc_cp = set_var_from_str(fc_str, fc_cp, &fc_value);

		/*
		 * 我们在这里重复几行代码，因为我们希望在出现任何语义错误
		 * 之前抛出任何结尾垃圾的语法错误。我们无法轻易地将这两种情况合并
		 * 在一起，因为我们不能对 NaN/Inf 应用 apply_typmod。
		 */
		while (*fc_cp)
		{
			if (!isspace((unsigned char) *fc_cp))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid input syntax for type %s: \"%s\"",
								"numeric", fc_str)));
			fc_cp++;
		}

		fc_apply_typmod(&fc_value, fc_typmod);

		fc_res = fc_make_result(&fc_value);
		fc_free_var(&fc_value);

		PG_RETURN_NUMERIC(fc_res);
	}

	/* 应该只剩下空格 */
	while (*fc_cp)
	{
		if (!isspace((unsigned char) *fc_cp))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"numeric", fc_str)));
		fc_cp++;
	}

	/* 如上，完成语法检查后抛出任何 typmod 错误 */
	fc_apply_typmod_special(fc_res, fc_typmod);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_out() -
 *
 *	用于数值数据类型的输出函数
 */
Datum numeric_out(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_x;
	char	   *fc_str;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			PG_RETURN_CSTRING(pstrdup("Infinity"));
		else if (NUMERIC_IS_NINF(fc_num))
			PG_RETURN_CSTRING(pstrdup("-Infinity"));
		else
			PG_RETURN_CSTRING(pstrdup("NaN"));
	}

	/*
	 * 以变量格式获取数字。
	 */
	fc_init_var_from_num(fc_num, &fc_x);

	fc_str = get_str_from_var(&fc_x);

	PG_RETURN_CSTRING(fc_str);
}

/*
 * numeric_is_nan() -
 *
 *	数值是否为 NaN？
 */
bool numeric_is_nan(Numeric fc_num)
{
	return NUMERIC_IS_NAN(fc_num);
}

/*
 * numeric_is_inf() -
 *
 *	数值是否为无穷大？
 */
bool numeric_is_inf(Numeric fc_num)
{
	return NUMERIC_IS_INF(fc_num);
}

/*
 * numeric_is_integral() -
 *
 *	数值是否为整数？
 */
static bool fc_numeric_is_integral(Numeric fc_num)
{
	NumericVar	fc_arg;

	/* 拒绝 NaN，但无穷大被认为是整数 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_NAN(fc_num))
			return false;
		return true;
	}

	/* 如果小数点右侧没有数字，则视为整数 */
	fc_init_var_from_num(fc_num, &fc_arg);

	return (fc_arg.ndigits == 0 || fc_arg.ndigits <= fc_arg.weight + 1);
}

/*
 * make_numeric_typmod() -
 *
 *	将数字精度和比例值打包到 typmod 中。高 16 位用于精度（虽然实际上并非所有这些位都是必要的，因为允许的最大精度为 1000）。低 16 位用于比例，但由于比例限制在 [-1000, 1000] 范围内，我们只使用这 16 位中的低 11 位，剩余的 5 位保持未设置，以备将来使用。
 *
 *	出于纯粹的历史原因，VARHDRSZ 然后被添加到结果中，因此高 16 位中未使用的空间并非都可以自由使用。（我们不能让结果溢出到负的 int32，因为系统的其他部分会将其解释为无效的 typmod。）
 */
static inline int32
fc_make_numeric_typmod(int fc_precision, int fc_scale)
{
	return ((fc_precision << 16) | (fc_scale & 0x7ff)) + VARHDRSZ;
}

/*
 * 因为偏移，有效的数字类型修饰符至少为 VARHDRSZ
 */
static inline bool fc_is_valid_numeric_typmod(int32 fc_typmod)
{
	return fc_typmod >= (int32) VARHDRSZ;
}

/*
 * numeric_typmod_precision() -
 *
 *	从数字类型修饰符中提取精度 --- 查看 make_numeric_typmod()。
 */
static inline int fc_numeric_typmod_precision(int32 fc_typmod)
{
	return ((fc_typmod - VARHDRSZ) >> 16) & 0xffff;
}

/*
 * numeric_typmod_scale() -
 *
 *	从数字类型修饰符中提取比例 --- 查看 make_numeric_typmod()。
 *
 *	注意，比例可能是负数，因此在解包时我们必须进行符号扩展。
 *	我们使用位操作 (x^1024)-1024 来实现这一点，该操作将 11 位的二进制补码数 x 扩展为符号。
 */
static inline int fc_numeric_typmod_scale(int32 fc_typmod)
{
	return (((fc_typmod - VARHDRSZ) & 0x7ff) ^ 1024) - 1024;
}

/*
 * numeric_maximum_size() -
 *
 *	给定类型修饰符的数字的最大大小，或者 -1 如果是无限/未知。
 */
int32 numeric_maximum_size(int32 fc_typmod)
{
	int			fc_precision;
	int			fc_numeric_digits;

	if (!fc_is_valid_numeric_typmod(fc_typmod))
		return -1;

	/* 精度（即，最大数字位数）位于类型修饰符的高位 */
	fc_precision = fc_numeric_typmod_precision(fc_typmod);

	/*
	 * 这个公式计算了为了存储指定数量的小数位，我们可能需要的最大 NumericDigits 数量。
	 * 因为权重以 NumericDigits 的数量而不是小数位的数量存储，所以第一个 NumericDigit
	 * 可能只包含一个小数位。因此，前两个小数位可能需要两个 NumericDigits 来存储，
	 * 但直到我们达到 DEC_DIGITS + 2 小数位时，我们才可能需要第三个 NumericDigit。
	 */
	fc_numeric_digits = (fc_precision + 2 * (DEC_DIGITS - 1)) / DEC_DIGITS;

	/*
	 * 在大多数情况下，数字的大小会比下面计算的值小，因为 varlena 头通常会缩减到
	 * 单个字节，然后再存储到磁盘，并且也可能能够使用短的数字头。
	 * 但我们在这里的工作是计算最坏情况。
	 */
	return NUMERIC_HDRSZ + (fc_numeric_digits * sizeof(NumericDigit));
}

/*
 * numeric_out_sci() -
 *
 *	以科学计数法输出数字数据类型的函数。
 */
char * numeric_out_sci(Numeric fc_num, int fc_scale)
{
	NumericVar	fc_x;
	char	   *fc_str;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			return pstrdup("Infinity");
		else if (NUMERIC_IS_NINF(fc_num))
			return pstrdup("-Infinity");
		else
			return pstrdup("NaN");
	}

	fc_init_var_from_num(fc_num, &fc_x);

	fc_str = get_str_from_var_sci(&fc_x, fc_scale);

	return fc_str;
}

/*
 * numeric_normalize() -
 *
 *	数字数据类型的输出函数，抑制不重要的尾随零，并去掉任何尾随的小数点。
 *	这样做的目的是生成仅在输入数字值相等时才相等的字符串。
 */
char * numeric_normalize(Numeric fc_num)
{
	NumericVar	fc_x;
	char	   *fc_str;
	int			fc_last;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			return pstrdup("Infinity");
		else if (NUMERIC_IS_NINF(fc_num))
			return pstrdup("-Infinity");
		else
			return pstrdup("NaN");
	}

	fc_init_var_from_num(fc_num, &fc_x);

	fc_str = get_str_from_var(&fc_x);

	/* 如果没有小数点，肯定没有需要去掉的东西。 */
	if (strchr(fc_str, '.') != NULL)
	{
		/*
		 * 在尾随的小数零上退回。由于有小数点，这个循环将安全终止。
		 */
		fc_last = strlen(fc_str) - 1;
		while (fc_str[fc_last] == '0')
			fc_last--;

		/* 如果最后是小数点，我们也想去掉它。 */
		if (fc_str[fc_last] == '.')
			fc_last--;

		/* 删除我们退回去的部分。 */
		fc_str[fc_last + 1] = '\0';
	}

	return fc_str;
}

/*
 *		numeric_recv			- 将外部二进制格式转换为数字
 *
 * 外部格式是一系列 int16：
 * ndigits, weight, sign, dscale, NumericDigits。
 */
Datum numeric_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			fc_typelem = PG_GETARG_OID(1);
#endif
	int32		fc_typmod = PG_GETARG_INT32(2);
	NumericVar	fc_value;
	Numeric		fc_res;
	int			fc_len,
				fc_i;

	init_var(&fc_value);

	fc_len = (uint16) pq_getmsgint(fc_buf, sizeof(uint16));

	fc_alloc_var(&fc_value, fc_len);

	fc_value.weight = (int16) pq_getmsgint(fc_buf, sizeof(int16));
	/* 我们允许任何 int16 作为权重 --- 可以吗？ */

	fc_value.sign = (uint16) pq_getmsgint(fc_buf, sizeof(uint16));
	if (!(fc_value.sign == NUMERIC_POS ||
		  fc_value.sign == NUMERIC_NEG ||
		  fc_value.sign == NUMERIC_NAN ||
		  fc_value.sign == NUMERIC_PINF ||
		  fc_value.sign == NUMERIC_NINF))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid sign in external \"numeric\" value")));

	fc_value.dscale = (uint16) pq_getmsgint(fc_buf, sizeof(uint16));
	if ((fc_value.dscale & NUMERIC_DSCALE_MASK) != fc_value.dscale)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
				 errmsg("invalid scale in external \"numeric\" value")));

	for (fc_i = 0; fc_i < fc_len; fc_i++)
	{
		NumericDigit fc_d = pq_getmsgint(fc_buf, sizeof(NumericDigit));

		if (fc_d < 0 || fc_d >= NBASE)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
					 errmsg("invalid digit in external \"numeric\" value")));
		fc_value.digits[fc_i] = fc_d;
	}

	/*
	 * 如果给定的 dscale 会隐藏任何数字，则截断这些数字。
	 * 我们可以选择抛出错误，但这会需要一堆额外的代码（大约与 trunc_var 涉及的代码量一样多），
	 * 并且可能导致客户端兼容性问题。小心不要将 trunc_var 应用到特殊值，
	 * 因为这可能会造成错误；反正我们不需要它，因为 make_result 将忽略除符号字段以外的所有字段。
	 *
	 * 在做到这一点之后，一定要检查类型修饰符限制。
	 */
	if (fc_value.sign == NUMERIC_POS ||
		fc_value.sign == NUMERIC_NEG)
	{
		fc_trunc_var(&fc_value, fc_value.dscale);

		fc_apply_typmod(&fc_value, fc_typmod);

		fc_res = fc_make_result(&fc_value);
	}
	else
	{
		/* apply_typmod_special 希望我们先创建 Numeric */
		fc_res = fc_make_result(&fc_value);

		fc_apply_typmod_special(fc_res, fc_typmod);
	}

	fc_free_var(&fc_value);

	PG_RETURN_NUMERIC(fc_res);
}

/*
 *		numeric_send			- 将数字转换为二进制格式
 */
Datum numeric_send(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_x;
	StringInfoData fc_buf;
	int			fc_i;

	fc_init_var_from_num(fc_num, &fc_x);

	pq_begintypsend(&fc_buf);

	pq_sendint16(&fc_buf, fc_x.ndigits);
	pq_sendint16(&fc_buf, fc_x.weight);
	pq_sendint16(&fc_buf, fc_x.sign);
	pq_sendint16(&fc_buf, fc_x.dscale);
	for (fc_i = 0; fc_i < fc_x.ndigits; fc_i++)
		pq_sendint16(&fc_buf, fc_x.digits[fc_i]);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*
 * numeric_support()
 *
 * 计划支持函数，用于 numeric() 长度强制转换函数。
 *
 * 拉平仅表示精度增加的调用。
 * 比例变化会改变每个数据，因此它们是不可优化的。一些值，例如 1E-1001，仅能适应于一个不受限制的数字，
 * 因此从一个不受限制的数字变为任何受限制的数字也是不可优化的。
 */
Datum numeric_support(PG_FUNCTION_ARGS)
{
	Node	   *fc_rawreq = (Node *) PG_GETARG_POINTER(0);
	Node	   *fc_ret = NULL;

	if (IsA(fc_rawreq, SupportRequestSimplify))
	{
		SupportRequestSimplify *fc_req = (SupportRequestSimplify *) fc_rawreq;
		FuncExpr   *fc_expr = fc_req->fcall;
		Node	   *fc_typmod;

		Assert(list_length(fc_expr->args) >= 2);

		fc_typmod = (Node *) lsecond(fc_expr->args);

		if (IsA(fc_typmod, Const) && !((Const *) fc_typmod)->constisnull)
		{
			Node	   *fc_source = (Node *) linitial(fc_expr->args);
			int32		fc_old_typmod = exprTypmod(fc_source);
			int32		fc_new_typmod = DatumGetInt32(((Const *) fc_typmod)->constvalue);
			int32		fc_old_scale = fc_numeric_typmod_scale(fc_old_typmod);
			int32		fc_new_scale = fc_numeric_typmod_scale(fc_new_typmod);
			int32		fc_old_precision = fc_numeric_typmod_precision(fc_old_typmod);
			int32		fc_new_precision = fc_numeric_typmod_precision(fc_new_typmod);

			/*
			 * 如果 new_typmod 无效，目标是无约束的；
			 * 这总是可以的。如果 old_typmod 有效，源是受约束的，
			 * 如果比例未改变且精度未减少，那么我们也是可以的。
			 * 有关详细信息，请参见函数头注释中的进一步说明。
			 */
			if (!fc_is_valid_numeric_typmod(fc_new_typmod) ||
				(fc_is_valid_numeric_typmod(fc_old_typmod) &&
				 fc_new_scale == fc_old_scale && fc_new_precision >= fc_old_precision))
				fc_ret = relabel_to_typmod(fc_source, fc_new_typmod);
		}
	}

	PG_RETURN_POINTER(fc_ret);
}

/*
 * numeric() -
 *
 *	这是一个特殊函数，由 Postgres 数据库系统在将值存储在元组属性中之前调用。
 *	属性的精度和比例必须应用于该值。
 */
Datum numeric		(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	int32		fc_typmod = PG_GETARG_INT32(1);
	Numeric		fc_new;
	int			fc_precision;
	int			fc_scale;
	int			fc_ddigits;
	int			fc_maxdigits;
	int			fc_dscale;
	NumericVar	fc_var;

	/*
	 * 处理 NaN 和无穷大：如果 apply_typmod_special 没有抱怨，
	 * 只需返回输入的副本。
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		fc_apply_typmod_special(fc_num, fc_typmod);
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));
	}

	/*
	 * 如果值不是有效的类型修饰符，简单地返回输入值的副本
	 */
	if (!fc_is_valid_numeric_typmod(fc_typmod))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	/*
	 * 从 typmod 值中获取精度和比例
	 */
	fc_precision = fc_numeric_typmod_precision(fc_typmod);
	fc_scale = fc_numeric_typmod_scale(fc_typmod);
	fc_maxdigits = fc_precision - fc_scale;

	/* 目标显示比例为非负数 */
	fc_dscale = Max(fc_scale, 0);

	/*
	 * 如果该数字确定在范围内，并且由于目标比例不需要进行四舍五入，只需复制输入并修改其比例字段，除非更大的比例迫使我们放弃短表示法。（注意我们假设现有的 dscale 是诚实的......）
	 */
	fc_ddigits = (NUMERIC_WEIGHT(fc_num) + 1) * DEC_DIGITS;
	if (fc_ddigits <= fc_maxdigits && fc_scale >= NUMERIC_DSCALE(fc_num)
		&& (NUMERIC_CAN_BE_SHORT(fc_dscale, NUMERIC_WEIGHT(fc_num))
			|| !NUMERIC_IS_SHORT(fc_num)))
	{
		fc_new = fc_duplicate_numeric(fc_num);
		if (NUMERIC_IS_SHORT(fc_num))
			fc_new->choice.n_short.n_header =
				(fc_num->choice.n_short.n_header & ~NUMERIC_SHORT_DSCALE_MASK)
				| (fc_dscale << NUMERIC_SHORT_DSCALE_SHIFT);
		else
			fc_new->choice.n_long.n_sign_dscale = NUMERIC_SIGN(fc_new) |
				((uint16) fc_dscale & NUMERIC_DSCALE_MASK);
		PG_RETURN_NUMERIC(fc_new);
	}

	/*
	 * 我们确实需要对事情进行调整 - 将数字拆包为一个变量，让 apply_typmod() 来处理它。
	 */
	init_var(&fc_var);

	set_var_from_num(fc_num, &fc_var);
	fc_apply_typmod(&fc_var, fc_typmod);
	fc_new = fc_make_result(&fc_var);

	fc_free_var(&fc_var);

	PG_RETURN_NUMERIC(fc_new);
}

Datum numerictypmodin(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_ta = PG_GETARG_ARRAYTYPE_P(0);
	int32	   *fc_tl;
	int			fc_n;
	int32		fc_typmod;

	fc_tl = ArrayGetIntegerTypmods(fc_ta, &fc_n);

	if (fc_n == 2)
	{
		if (fc_tl[0] < 1 || fc_tl[0] > NUMERIC_MAX_PRECISION)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("NUMERIC precision %d must be between 1 and %d",
							fc_tl[0], NUMERIC_MAX_PRECISION)));
		if (fc_tl[1] < NUMERIC_MIN_SCALE || fc_tl[1] > NUMERIC_MAX_SCALE)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("NUMERIC scale %d must be between %d and %d",
							fc_tl[1], NUMERIC_MIN_SCALE, NUMERIC_MAX_SCALE)));
		fc_typmod = fc_make_numeric_typmod(fc_tl[0], fc_tl[1]);
	}
	else if (fc_n == 1)
	{
		if (fc_tl[0] < 1 || fc_tl[0] > NUMERIC_MAX_PRECISION)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("NUMERIC precision %d must be between 1 and %d",
							fc_tl[0], NUMERIC_MAX_PRECISION)));
		/* 比例默认为零 */
		fc_typmod = fc_make_numeric_typmod(fc_tl[0], 0);
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid NUMERIC type modifier")));
		fc_typmod = 0;				/* 保持编译器安静 */
	}

	PG_RETURN_INT32(fc_typmod);
}

Datum numerictypmodout(PG_FUNCTION_ARGS)
{
	int32		fc_typmod = PG_GETARG_INT32(0);
	char	   *fc_res = (char *) palloc(64);

	if (fc_is_valid_numeric_typmod(fc_typmod))
		snprintf(fc_res, 64, "(%d,%d)",
				 fc_numeric_typmod_precision(fc_typmod),
				 fc_numeric_typmod_scale(fc_typmod));
	else
		*fc_res = '\0';

	PG_RETURN_CSTRING(fc_res);
}


/* ----------------------------------------------------------------------
 *
 * 符号操作、舍入等
 *
 * ----------------------------------------------------------------------
 */

Datum numeric_abs(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;

	/*
	 * 直接在打包格式上以简单的方式执行
	 */
	fc_res = fc_duplicate_numeric(fc_num);

	if (NUMERIC_IS_SHORT(fc_num))
		fc_res->choice.n_short.n_header =
			fc_num->choice.n_short.n_header & ~NUMERIC_SHORT_SIGN_MASK;
	else if (NUMERIC_IS_SPECIAL(fc_num))
	{
		/* 这将 -Inf 转换为 Inf，并且不会影响 NaN */
		fc_res->choice.n_short.n_header =
			fc_num->choice.n_short.n_header & ~NUMERIC_INF_SIGN_MASK;
	}
	else
		fc_res->choice.n_long.n_sign_dscale = NUMERIC_POS | NUMERIC_DSCALE(fc_num);

	PG_RETURN_NUMERIC(fc_res);
}


Datum numeric_uminus(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;

	/*
	 * 直接在打包格式上以简单的方式执行
	 */
	fc_res = fc_duplicate_numeric(fc_num);

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		/* 如果是 Inf 或 -Inf，则翻转符号 */
		if (!NUMERIC_IS_NAN(fc_num))
			fc_res->choice.n_short.n_header =
				fc_num->choice.n_short.n_header ^ NUMERIC_INF_SIGN_MASK;
	}

	/*
	 * 打包格式被认为总是完全去掉零数字。因此
	 * 一旦我们消除了特殊值，我们可以通过
	 * 完全没有数字来识别零。对零什么都不做。
	 */
	else if (NUMERIC_NDIGITS(fc_num) != 0)
	{
		/* 否则，翻转符号 */
		if (NUMERIC_IS_SHORT(fc_num))
			fc_res->choice.n_short.n_header =
				fc_num->choice.n_short.n_header ^ NUMERIC_SHORT_SIGN_MASK;
		else if (NUMERIC_SIGN(fc_num) == NUMERIC_POS)
			fc_res->choice.n_long.n_sign_dscale =
				NUMERIC_NEG | NUMERIC_DSCALE(fc_num);
		else
			fc_res->choice.n_long.n_sign_dscale =
				NUMERIC_POS | NUMERIC_DSCALE(fc_num);
	}

	PG_RETURN_NUMERIC(fc_res);
}


Datum numeric_uplus(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);

	PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));
}


/*
 * numeric_sign_internal() -
 *
 * 如果参数小于 0 返回 -1，如果参数等于
 * 0 返回 0，如果参数大于零返回 1。调用方必须
 * 处理 NaN 情况，但我们可以在这里处理无穷大。
 */
static int fc_numeric_sign_internal(Numeric fc_num)
{
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		Assert(!NUMERIC_IS_NAN(fc_num));
		/* 必须是 Inf 或 -Inf */
		if (NUMERIC_IS_PINF(fc_num))
			return 1;
		else
			return -1;
	}

	/*
	 * 打包格式被认为总是完全去掉零数字。因此
	 * 一旦我们消除了特殊值，我们可以通过
	 * 完全没有数字来识别零。
	 */
	else if (NUMERIC_NDIGITS(fc_num) == 0)
		return 0;
	else if (NUMERIC_SIGN(fc_num) == NUMERIC_NEG)
		return -1;
	else
		return 1;
}

/*
 * numeric_sign() -
 *
 * 如果参数小于 0 返回 -1，如果参数等于
 * 0 返回 0，如果参数大于零返回 1。
 */
Datum numeric_sign(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);

	/*
	 * 处理 NaN（可以正常处理无穷大）
	 */
	if (NUMERIC_IS_NAN(fc_num))
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	switch (fc_numeric_sign_internal(fc_num))
	{
		case 0:
			PG_RETURN_NUMERIC(fc_make_result(&const_zero));
		case 1:
			PG_RETURN_NUMERIC(fc_make_result(&const_one));
		case -1:
			PG_RETURN_NUMERIC(fc_make_result(&const_minus_one));
	}

	Assert(false);
	return (Datum) 0;
}


/*
 * numeric_round() -
 *
 * 将值舍入为小数点后有 'scale' 位数字。
 * 我们允许负的 'scale'，意味着在小数点
 * 前进行舍入---Oracle 以这种方式解释舍入。
 */
Datum numeric_round(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	int32		fc_scale = PG_GETARG_INT32(1);
	Numeric		fc_res;
	NumericVar	fc_arg;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	/*
	 * 限制小数位数以避免计算中的可能溢出。
	 *
	 * 这些限制基于 Numeric 值可以在小数点前后
	 * 具有的最大数字，但我们必须预留一个额外的
	 * 小数点前的数字，以防最高有效数字向上舍入；
	 * 我们必须检查这是否会导致 Numeric 溢出。
	 */
	fc_scale = Max(fc_scale, -(NUMERIC_WEIGHT_MAX + 1) * DEC_DIGITS - 1);
	fc_scale = Min(fc_scale, NUMERIC_DSCALE_MAX);

	/*
	 * 解包参数并在正确的数字位置舍入
	 */
	init_var(&fc_arg);
	set_var_from_num(fc_num, &fc_arg);

	fc_round_var(&fc_arg, fc_scale);

	/* 我们不允许负的输出 dscale */
	if (fc_scale < 0)
		fc_arg.dscale = 0;

	/*
	 * 返回舍入后的结果
	 */
	fc_res = fc_make_result(&fc_arg);

	fc_free_var(&fc_arg);
	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_trunc() -
 *
 * 将值截断为小数点后有 'scale' 位数字。
 * 我们允许负的 'scale'，意味着在小数点
 * 前进行截断---Oracle 以这种方式解释截断。
 */
Datum numeric_trunc(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	int32		fc_scale = PG_GETARG_INT32(1);
	Numeric		fc_res;
	NumericVar	fc_arg;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	/*
	 * 限制小数位数以避免计算中的可能溢出。
	 *
	 * 这些限制基于 Numeric 值可以在小数点前后
	 * 具有的最大数字。
	 */
	fc_scale = Max(fc_scale, -(NUMERIC_WEIGHT_MAX + 1) * DEC_DIGITS);
	fc_scale = Min(fc_scale, NUMERIC_DSCALE_MAX);

	/*
	 * 解包参数并在正确的数字位置截断
	 */
	init_var(&fc_arg);
	set_var_from_num(fc_num, &fc_arg);

	fc_trunc_var(&fc_arg, fc_scale);

	/* 我们不允许负的输出 dscale */
	if (fc_scale < 0)
		fc_arg.dscale = 0;

	/*
	 * 返回截断后的结果
	 */
	fc_res = fc_make_result(&fc_arg);

	fc_free_var(&fc_arg);
	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_ceil() -
 *
 * 返回大于或等于参数的最小整数
 */
Datum numeric_ceil(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_result;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	fc_init_var_from_num(fc_num, &fc_result);
	fc_ceil_var(&fc_result, &fc_result);

	fc_res = fc_make_result(&fc_result);
	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_floor() -
 *
 * 返回小于或等于参数的最大整数
 */
Datum numeric_floor(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_result;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	fc_init_var_from_num(fc_num, &fc_result);
	fc_floor_var(&fc_result, &fc_result);

	fc_res = fc_make_result(&fc_result);
	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * generate_series_numeric() -
 *
 * 生成数值序列。
 */
Datum generate_series_numeric(PG_FUNCTION_ARGS)
{
	return generate_series_step_numeric(fcinfo);
}

Datum generate_series_step_numeric(PG_FUNCTION_ARGS)
{
	generate_series_numeric_fctx *fc_fctx;
	FuncCallContext *fc_funcctx;
	MemoryContext fc_oldcontext;

	if (SRF_IS_FIRSTCALL())
	{
		Numeric		fc_start_num = PG_GETARG_NUMERIC(0);
		Numeric		fc_stop_num = PG_GETARG_NUMERIC(1);
		NumericVar	fc_steploc = const_one;

		/* 拒绝 NaN 和无穷大作为起始和结束值 */
		if (NUMERIC_IS_SPECIAL(fc_start_num))
		{
			if (NUMERIC_IS_NAN(fc_start_num))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("start value cannot be NaN")));
			else
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("start value cannot be infinity")));
		}
		if (NUMERIC_IS_SPECIAL(fc_stop_num))
		{
			if (NUMERIC_IS_NAN(fc_stop_num))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("stop value cannot be NaN")));
			else
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("stop value cannot be infinity")));
		}

		/* 检查我们是否被提供了显式步长 */
		if (PG_NARGS() == 3)
		{
			Numeric		fc_step_num = PG_GETARG_NUMERIC(2);

			if (NUMERIC_IS_SPECIAL(fc_step_num))
			{
				if (NUMERIC_IS_NAN(fc_step_num))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("step size cannot be NaN")));
				else
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("step size cannot be infinity")));
			}

			fc_init_var_from_num(fc_step_num, &fc_steploc);

			if (fc_cmp_var(&fc_steploc, &const_zero) == 0)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("step size cannot equal zero")));
		}

		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		/*
		 * 切换到适合多个函数调用的内存上下文。
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 为用户上下文分配内存 */
		fc_fctx = (generate_series_numeric_fctx *)
			palloc(sizeof(generate_series_numeric_fctx));

		/*
		 * 使用 fctx 来保持状态。将当前值种子为
		 * 原始起始值。我们必须复制 start_num 和 stop_num
		 * 的值，而不是指向它们，因为我们可能已经在
		 * 每次调用的上下文中进行了去吐槽。
		 */
		init_var(&fc_fctx->current);
		init_var(&fc_fctx->stop);
		init_var(&fc_fctx->step);

		set_var_from_num(fc_start_num, &fc_fctx->current);
		set_var_from_num(fc_stop_num, &fc_fctx->stop);
		set_var_from_var(&fc_steploc, &fc_fctx->step);

		fc_funcctx->user_fctx = fc_fctx;
		MemoryContextSwitchTo(fc_oldcontext);
	}

	/* 每次调用函数时完成的工作 */
	fc_funcctx = SRF_PERCALL_SETUP();

	/*
	 * 获取保存的状态并将当前状态用作本次
	 * 迭代的结果。
	 */
	fc_fctx = fc_funcctx->user_fctx;

	if ((fc_fctx->step.sign == NUMERIC_POS &&
		 fc_cmp_var(&fc_fctx->current, &fc_fctx->stop) <= 0) ||
		(fc_fctx->step.sign == NUMERIC_NEG &&
		 fc_cmp_var(&fc_fctx->current, &fc_fctx->stop) >= 0))
	{
		Numeric		fc_result = fc_make_result(&fc_fctx->current);

		/* 切换到适合迭代计算的内存上下文 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 增加当前值以准备下一次迭代 */
		add_var(&fc_fctx->current, &fc_fctx->step, &fc_fctx->current);
		MemoryContextSwitchTo(fc_oldcontext);

		/* 当还有更多内容要发送时执行 */
		SRF_RETURN_NEXT(fc_funcctx, NumericGetDatum(fc_result));
	}
	else
		/* 当没有更多内容时执行 */
		SRF_RETURN_DONE(fc_funcctx);
}


/*
 * 实现 SQL2003 定义的 numeric 版本 width_bucket() 函数。
 * 另见 width_bucket_float8()。
 *
 * 'bound1' 和 'bound2' 分别是
 * 直方图范围的下界和上界。'count' 是
 * 直方图中的桶数。width_bucket() 返回一个整数，表示
 * 'operand' 在指定特征的等宽直方图中的桶号。
 * 小于下界的操作数分配给桶 0。
 * 大于上界的操作数将分配给一个额外的桶
 * （编号为 count+1）。我们不允许任何数值参数为 "NaN"。
 */
Datum width_bucket_numeric(PG_FUNCTION_ARGS)
{
	Numeric		fc_operand = PG_GETARG_NUMERIC(0);
	Numeric		fc_bound1 = PG_GETARG_NUMERIC(1);
	Numeric		fc_bound2 = PG_GETARG_NUMERIC(2);
	int32		fc_count = PG_GETARG_INT32(3);
	NumericVar	fc_count_var;
	NumericVar	fc_result_var;
	int32		fc_result;

	if (fc_count <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
				 errmsg("count must be greater than zero")));

	if (NUMERIC_IS_SPECIAL(fc_operand) ||
		NUMERIC_IS_SPECIAL(fc_bound1) ||
		NUMERIC_IS_SPECIAL(fc_bound2))
	{
		if (NUMERIC_IS_NAN(fc_operand) ||
			NUMERIC_IS_NAN(fc_bound1) ||
			NUMERIC_IS_NAN(fc_bound2))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
					 errmsg("operand, lower bound, and upper bound cannot be NaN")));
		/* 我们允许 "operand" 为无穷大；cmp_numerics 将处理 */
		if (NUMERIC_IS_INF(fc_bound1) || NUMERIC_IS_INF(fc_bound2))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
					 errmsg("lower and upper bounds must be finite")));
	}

	init_var(&fc_result_var);
	init_var(&fc_count_var);

	/* 将 'count' 转换为数字，以便后续使用更方便 */
	fc_int64_to_numericvar((int64) fc_count, &fc_count_var);

	switch (fc_cmp_numerics(fc_bound1, fc_bound2))
	{
		case 0:
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
					 errmsg("lower bound cannot equal upper bound")));
			break;

			/* 边界1 < 边界2 */
		case -1:
			if (fc_cmp_numerics(fc_operand, fc_bound1) < 0)
				set_var_from_var(&const_zero, &fc_result_var);
			else if (fc_cmp_numerics(fc_operand, fc_bound2) >= 0)
				add_var(&fc_count_var, &const_one, &fc_result_var);
			else
				fc_compute_bucket(fc_operand, fc_bound1, fc_bound2, &fc_count_var, false,
							   &fc_result_var);
			break;

			/* 边界1 > 边界2 */
		case 1:
			if (fc_cmp_numerics(fc_operand, fc_bound1) > 0)
				set_var_from_var(&const_zero, &fc_result_var);
			else if (fc_cmp_numerics(fc_operand, fc_bound2) <= 0)
				add_var(&fc_count_var, &const_one, &fc_result_var);
			else
				fc_compute_bucket(fc_operand, fc_bound1, fc_bound2, &fc_count_var, true,
							   &fc_result_var);
			break;
	}

	/* 如果结果超出合法的 int4 范围，我们在这里报告错误 */
	if (!fc_numericvar_to_int32(&fc_result_var, &fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("integer out of range")));

	fc_free_var(&fc_count_var);
	fc_free_var(&fc_result_var);

	PG_RETURN_INT32(fc_result);
}

/*
 * 如果 'operand' 不在桶范围之外，确定其正确的
 * 桶去。该函数执行的计算直接来自 SQL2003 规范。请注意，
 * 我们在除法前乘以计数，以避免不必要的舍入错误。
 */
static void fc_compute_bucket(Numeric fc_operand, Numeric fc_bound1, Numeric fc_bound2,
			   const NumericVar *fc_count_var, bool fc_reversed_bounds,
			   NumericVar *fc_result_var)
{
	NumericVar	fc_bound1_var;
	NumericVar	fc_bound2_var;
	NumericVar	fc_operand_var;

	fc_init_var_from_num(fc_bound1, &fc_bound1_var);
	fc_init_var_from_num(fc_bound2, &fc_bound2_var);
	fc_init_var_from_num(fc_operand, &fc_operand_var);

	if (!fc_reversed_bounds)
	{
		fc_sub_var(&fc_operand_var, &fc_bound1_var, &fc_operand_var);
		fc_sub_var(&fc_bound2_var, &fc_bound1_var, &fc_bound2_var);
	}
	else
	{
		fc_sub_var(&fc_bound1_var, &fc_operand_var, &fc_operand_var);
		fc_sub_var(&fc_bound1_var, &fc_bound2_var, &fc_bound2_var);
	}

	fc_mul_var(&fc_operand_var, fc_count_var, &fc_operand_var,
			fc_operand_var.dscale + fc_count_var->dscale);
	fc_div_var(&fc_operand_var, &fc_bound2_var, fc_result_var,
			fc_select_div_scale(&fc_operand_var, &fc_bound2_var), true);
	add_var(fc_result_var, &const_one, fc_result_var);
	fc_floor_var(fc_result_var, fc_result_var);

	fc_free_var(&fc_bound1_var);
	fc_free_var(&fc_bound2_var);
	fc_free_var(&fc_operand_var);
}

/* ----------------------------------------------------------------------
 *
 * 比较函数
 *
 * 注意：btree 索引需要这些例程来避免内存泄漏；因此，
 * 请小心释放已经处理过的 Toast 数据的工作副本。大多数地方不需要
 * 这么小心。
 *
 * 排序支持：
 *
 * 我们实现排序支持策略例程，以便获得缩写的好处。普通的数字比较
 * 由于 palloc/pfree 循环（因为需要为对齐而对打包值进行解包）
 * 可能相当慢；虽然这本身可以进行优化，但缩写策略在许多常见情况下
 * 提供了更多的速度提升。
 *
 * 为缩写形式使用了两种不同的表示，一种是 int32，另一种是 int64，
 * 任意一个适合一个值传递的 Datum。在这两种情况下，
 * 表示相对于原始值是取反的，因为我们使用
 * 最大的负值表示 NaN，它的排序优于其他值。我们将数字的绝对值
 * 转换为一个 31 位或 63 位的正值，然后如果原始数字为正，则取反。
 *
 * 如果缩写基数低于行数的 0.01%（每 10,000 个非空行 1 个），我们将中止
 * 缩写过程。实际的盈亏平衡点稍低，或许每 30,000 个（在每 100,000
 * 个时有非常小的惩罚），但我们不想在首次测试中止之前积累过多的
 * 缩写值，因此我们取稍微悲观的数字。我们未尝试估算实际值的基数，
 * 因为它在此的成本模型中没有作用（如果缩写相等，比较相等和不相等的
 * 基础值的成本是相当的）。如果估算的基数达到 100,000，我们就不检查
 * 中止（节省了哈希开销）；这将足以支持数十亿行，同时不会比持平更糟。
 *
 * ----------------------------------------------------------------------
 */

/*
 * 排序支持策略例程。
 */
Datum numeric_sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);

	fc_ssup->comparator = fc_numeric_fast_cmp;

	if (fc_ssup->abbreviate)
	{
		NumericSortSupport *fc_nss;
		MemoryContext fc_oldcontext = MemoryContextSwitchTo(fc_ssup->ssup_cxt);

		fc_nss = palloc(sizeof(NumericSortSupport));

		/*
		 * 为处理未对齐的打包值分配一个缓冲区，此外还有
		 * 支持结构
		 */
		fc_nss->buf = palloc(VARATT_SHORT_MAX + VARHDRSZ + 1);

		fc_nss->input_count = 0;
		fc_nss->estimating = true;
		initHyperLogLog(&fc_nss->abbr_card, 10);

		fc_ssup->ssup_extra = fc_nss;

		fc_ssup->abbrev_full_comparator = fc_ssup->comparator;
		fc_ssup->comparator = fc_numeric_cmp_abbrev;
		fc_ssup->abbrev_converter = fc_numeric_abbrev_convert;
		fc_ssup->abbrev_abort = fc_numeric_abbrev_abort;

		MemoryContextSwitchTo(fc_oldcontext);
	}

	PG_RETURN_VOID();
}

/*
 * 缩写一个数字数据项，处理 NaN 和解包
 * （必须不泄漏内存！）
 */
static Datum fc_numeric_abbrev_convert(Datum fc_original_datum, SortSupport fc_ssup)
{
	NumericSortSupport *fc_nss = fc_ssup->ssup_extra;
	void	   *fc_original_varatt = PG_DETOAST_DATUM_PACKED(fc_original_datum);
	Numeric		fc_value;
	Datum		fc_result;

	fc_nss->input_count += 1;

	/*
	 * 这是为了处理打包数据项，而无需进行 palloc/pfree 循环；
	 * 我们保持并重用一个足够大的缓冲区，以处理任何短数据项。
	 */
	if (VARATT_IS_SHORT(fc_original_varatt))
	{
		void	   *fc_buf = fc_nss->buf;
		Size		fc_sz = VARSIZE_SHORT(fc_original_varatt) - VARHDRSZ_SHORT;

		Assert(fc_sz <= VARATT_SHORT_MAX - VARHDRSZ_SHORT);

		SET_VARSIZE(fc_buf, VARHDRSZ + fc_sz);
		memcpy(VARDATA(fc_buf), VARDATA_SHORT(fc_original_varatt), fc_sz);

		fc_value = (Numeric) fc_buf;
	}
	else
		fc_value = (Numeric) fc_original_varatt;

	if (NUMERIC_IS_SPECIAL(fc_value))
	{
		if (NUMERIC_IS_PINF(fc_value))
			fc_result = NUMERIC_ABBREV_PINF;
		else if (NUMERIC_IS_NINF(fc_value))
			fc_result = NUMERIC_ABBREV_NINF;
		else
			fc_result = NUMERIC_ABBREV_NAN;
	}
	else
	{
		NumericVar	fc_var;

		fc_init_var_from_num(fc_value, &fc_var);

		fc_result = fc_numeric_abbrev_convert_var(&fc_var, fc_nss);
	}

	/* 应仅在外部/压缩的 Toast 数据时发生 */
	if ((Pointer) fc_original_varatt != DatumGetPointer(fc_original_datum))
		pfree(fc_original_varatt);

	return fc_result;
}

/*
 * 考虑是否中止缩写。
 *
 * 我们不关注未缩写数据的基数。这样做没有理由：与文本不同，
 * 我们没有快速检查相等值的方式，因此每当缩写相等时，不论
 * 基础值是否也相等，我们都需要承担全部开销。
 */
static bool fc_numeric_abbrev_abort(int fc_memtupcount, SortSupport fc_ssup)
{
	NumericSortSupport *fc_nss = fc_ssup->ssup_extra;
	double		fc_abbr_card;

	if (fc_memtupcount < 10000 || fc_nss->input_count < 10000 || !fc_nss->estimating)
		return false;

	fc_abbr_card = estimateHyperLogLog(&fc_nss->abbr_card);

	/*
	 * 如果我们有超过 100,000 个不同值，那么即便我们在排序成千上亿行时，
	 * 可能仍能持平，而撤消如此多的缩写行可能并不值得。到那个点
	 * 停止计数。
	 */
	if (fc_abbr_card > 100000.0)
	{
#ifdef TRACE_SORT
		if (trace_sort)
			elog(LOG,
				 "numeric_abbrev: estimation ends at cardinality %f"
				 " after " INT64_FORMAT " values (%d rows)",
				 fc_abbr_card, fc_nss->input_count, fc_memtupcount);
#endif
		fc_nss->estimating = false;
		return false;
	}

	/*
	 * 目标最小基数约为每 10,000 个非空输入 1 个。 （
	 * 盈亏平衡点介于每 100,000 行（缩写有非常轻微的惩罚）和每
	 * 10,000 行（在可测百分比上获利）之间。）我们使用相对悲观的
	 * 10,000 阈值，并添加一个 0.5 行的偏差因素，因为它使我们能够
	 * 在真正病态数据中更早中止，即在前 10,000 个（非空）行中
	 * 恰好有一个缩写值的情况下。
	 */
	if (fc_abbr_card < fc_nss->input_count / 10000.0 + 0.5)
	{
#ifdef TRACE_SORT
		if (trace_sort)
			elog(LOG,
				 "numeric_abbrev: aborting abbreviation at cardinality %f"
				 " below threshold %f after " INT64_FORMAT " values (%d rows)",
				 fc_abbr_card, fc_nss->input_count / 10000.0 + 0.5,
				 fc_nss->input_count, fc_memtupcount);
#endif
		return true;
	}

#ifdef TRACE_SORT
	if (trace_sort)
		elog(LOG,
			 "numeric_abbrev: cardinality %f"
			 " after " INT64_FORMAT " values (%d rows)",
			 fc_abbr_card, fc_nss->input_count, fc_memtupcount);
#endif

	return false;
}

/*
 * 非 fmgr 接口的比较例程，以便允许排序支持省略
 * fmgr 调用。考虑到数字比较的速度慢，这里的节省很小，但它是
 * 执行缩写时所需的排序支持 API 的一部分。
 *
 * 通过使用持久缓冲区对齐短 varlena 输入，可以节省两个 palloc/pfree
 * 循环，但目前为止这并未被认为是值得的努力。
 */
static int fc_numeric_fast_cmp(Datum fc_x, Datum fc_y, SortSupport fc_ssup)
{
	Numeric		fc_nx = DatumGetNumeric(fc_x);
	Numeric		fc_ny = DatumGetNumeric(fc_y);
	int			fc_result;

	fc_result = fc_cmp_numerics(fc_nx, fc_ny);

	if ((Pointer) fc_nx != DatumGetPointer(fc_x))
		pfree(fc_nx);
	if ((Pointer) fc_ny != DatumGetPointer(fc_y))
		pfree(fc_ny);

	return fc_result;
}

/*
 * 比较值的缩写。（缩写可能在真实值不同的情况下相等，但如果
 * 缩写不同，它们必须反映真实值的排序。）
 */
static int fc_numeric_cmp_abbrev(Datum fc_x, Datum fc_y, SortSupport fc_ssup)
{
	/*
	 * 注意：这故意是反向的，因为缩写相对于原始值是取反的，
	 * 以处理 NaN/无穷大情况。
	 */
	if (DatumGetNumericAbbrev(fc_x) < DatumGetNumericAbbrev(fc_y))
		return 1;
	if (DatumGetNumericAbbrev(fc_x) > DatumGetNumericAbbrev(fc_y))
		return -1;
	return 0;
}


/*
 * 根据可用的位大小对 NumericVar 进行缩写。
 *
 * 31 位值的构造如下：
 *
 *	0 + 7 位数字权重 + 24 位数字值
 *
 * 其中数字权重以单个十进制数字表示，而不是数字词，并以超额-44 表示法存储[1]。24 位数字值是转换为二进制的值的 7 个最重要的十进制数字。权重超出可表示范围的值被四舍五入为零（这也用来表示实际的零）或 0x7FFFFFFF（否则无法出现）。因此，当值超出 10^-44 到 10^83 的范围时，缩写无法获得任何优势，这被认为不是一个严重的限制，或者当值的数量级相同且前 7 个十进制数字相等时，这被认为是一个不可避免的限制，考虑到可用的位数。（再偷取三个位来比较另一个数字将使可表示权重的范围缩小 8 倍，这开始看起来像是一个真正的限制因素。）
 *
 * （超额的值 44 本质上是任意的）
 *
 * 63 位值的构造如下：
 *
 *	0 + 7 位权重 + 4 x 14 位打包数字词
 *
 * 在这种情况下，权重再次以超额-44 存储，但这一次它是数字词中的原始权重（即，10000 的幂）。值的前四个数字词（如果存在；根据需要假设尾随零）被打包成每个 14 位以形成其余值。同样，超出范围的值被四舍五入到 0 或 0x7FFFFFFFFFFFFFFF。此情况下的可表示范围是 10^-176 到 10^332，这被认为在所有实际情况下都足够好，而 4 个词的比较意味着至少比较 13 个十进制数字，这被认为是在计算缩写的有效性和效率之间的合理折中。
 *
 * （这里超额的值 44 更加任意，它的选择只是为了与 31 位情况下使用的值相匹配）
 *
 * [1] - 超额-k 表示法意味着通过加上 'k' 来偏移值，然后将其视为无符号，因此最小的可表示值以所有位为零存储。这允许简单的比较在复合值上工作。
 */

#if NUMERIC_ABBREV_BITS == 64

static Datum fc_numeric_abbrev_convert_var(const NumericVar *fc_var, NumericSortSupport *fc_nss)
{
	int			fc_ndigits = fc_var->ndigits;
	int			fc_weight = fc_var->weight;
	int64		fc_result;

	if (fc_ndigits == 0 || fc_weight < -44)
	{
		fc_result = 0;
	}
	else if (fc_weight > 83)
	{
		fc_result = PG_INT64_MAX;
	}
	else
	{
		fc_result = ((int64) (fc_weight + 44) << 56);

		switch (fc_ndigits)
		{
			default:
				fc_result |= ((int64) fc_var->digits[3]);
				/* FALLTHROUGH */
			case 3:
				fc_result |= ((int64) fc_var->digits[2]) << 14;
				/* FALLTHROUGH */
			case 2:
				fc_result |= ((int64) fc_var->digits[1]) << 28;
				/* FALLTHROUGH */
			case 1:
				fc_result |= ((int64) fc_var->digits[0]) << 42;
				break;
		}
	}

	/* abbrev 相对于原值是取反的 */
	if (fc_var->sign == NUMERIC_POS)
		fc_result = -fc_result;

	if (fc_nss->estimating)
	{
		uint32		fc_tmp = ((uint32) fc_result
						   ^ (uint32) ((uint64) fc_result >> 32));

		addHyperLogLog(&fc_nss->abbr_card, DatumGetUInt32(hash_uint32(fc_tmp)));
	}

	return NumericAbbrevGetDatum(fc_result);
}

#endif							/* NUMERIC_ABBREV_BITS == 64 */

#if NUMERIC_ABBREV_BITS == 32

static Datum numeric_abbrev_convert_var(const NumericVar *var, NumericSortSupport *nss)
{
	int			ndigits = var->ndigits;
	int			weight = var->weight;
	int32		result;

	if (ndigits == 0 || weight < -11)
	{
		result = 0;
	}
	else if (weight > 20)
	{
		result = PG_INT32_MAX;
	}
	else
	{
		NumericDigit nxt1 = (ndigits > 1) ? var->digits[1] : 0;

		weight = (weight + 11) * 4;

		result = var->digits[0];

		/*
		 * “result” 现在有 1 到 4 个非零十进制数字。我们打包更多数字，使总数达到 7（我们能在 24 位中放下的最大数字）
		 */

		if (result > 999)
		{
			/* 已经有 4 个数字，再加 3 个 */
			result = (result * 1000) + (nxt1 / 10);
			weight += 3;
		}
		else if (result > 99)
		{
			/* 已经有 3 个数字，再加 4 个 */
			result = (result * 10000) + nxt1;
			weight += 2;
		}
		else if (result > 9)
		{
			NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0;

			/* 已经有 2 个数字，再加 5 个 */
			result = (result * 100000) + (nxt1 * 10) + (nxt2 / 1000);
			weight += 1;
		}
		else
		{
			NumericDigit nxt2 = (ndigits > 2) ? var->digits[2] : 0;

			/* 已经有 1 个数字，再加 6 个 */
			result = (result * 1000000) + (nxt1 * 100) + (nxt2 / 100);
		}

		result = result | (weight << 24);
	}

	/* abbrev 相对于原值是取反的 */
	if (var->sign == NUMERIC_POS)
		result = -result;

	if (nss->estimating)
	{
		uint32		tmp = (uint32) result;

		addHyperLogLog(&nss->abbr_card, DatumGetUInt32(hash_uint32(tmp)));
	}

	return NumericAbbrevGetDatum(result);
}

#endif							/* NUMERIC_ABBREV_BITS == 32 */

/*
 * 普通（非排序支持）比较如下。
 */

Datum numeric_cmp(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	int			fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2);

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_INT32(fc_result);
}


Datum numeric_eq(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) == 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum numeric_ne(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) != 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum numeric_gt(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) > 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum numeric_ge(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) >= 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum numeric_lt(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) < 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

Datum numeric_le(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	bool		fc_result;

	fc_result = fc_cmp_numerics(fc_num1, fc_num2) <= 0;

	PG_FREE_IF_COPY(fc_num1, 0);
	PG_FREE_IF_COPY(fc_num2, 1);

	PG_RETURN_BOOL(fc_result);
}

static int fc_cmp_numerics(Numeric fc_num1, Numeric fc_num2)
{
	int			fc_result;

	/*
	 * 我们认为所有 NAN 都等于并且大于任何非 NAN（包括
	 * 无穷大）。这在某种程度上是任意的；重要的是要保持
	 * 一致的排序顺序。
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1))
	{
		if (NUMERIC_IS_NAN(fc_num1))
		{
			if (NUMERIC_IS_NAN(fc_num2))
				fc_result = 0;		/* NAN = NAN */
			else
				fc_result = 1;		/* NAN > 非NAN */
		}
		else if (NUMERIC_IS_PINF(fc_num1))
		{
			if (NUMERIC_IS_NAN(fc_num2))
				fc_result = -1;	/* PINF < NAN */
			else if (NUMERIC_IS_PINF(fc_num2))
				fc_result = 0;		/* PINF = PINF */
			else
				fc_result = 1;		/* PINF > 其他任何东西 */
		}
		else					/* num1 必须是 NINF */
		{
			if (NUMERIC_IS_NINF(fc_num2))
				fc_result = 0;		/* NINF = NINF */
			else
				fc_result = -1;	/* NINF < 其他任何值 */
		}
	}
	else if (NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NINF(fc_num2))
			fc_result = 1;			/* normal > NINF */
		else
			fc_result = -1;		/* normal < NAN 或 PINF */
	}
	else
	{
		fc_result = fc_cmp_var_common(NUMERIC_DIGITS(fc_num1), NUMERIC_NDIGITS(fc_num1),
								NUMERIC_WEIGHT(fc_num1), NUMERIC_SIGN(fc_num1),
								NUMERIC_DIGITS(fc_num2), NUMERIC_NDIGITS(fc_num2),
								NUMERIC_WEIGHT(fc_num2), NUMERIC_SIGN(fc_num2));
	}

	return fc_result;
}

/*
 * in_range 支持函数，用于数字。
 */
Datum in_range_numeric_numeric(PG_FUNCTION_ARGS)
{
	Numeric		fc_val = PG_GETARG_NUMERIC(0);
	Numeric		fc_base = PG_GETARG_NUMERIC(1);
	Numeric		fc_offset = PG_GETARG_NUMERIC(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	bool		fc_result;

	/*
	 * 拒绝负数（包括 -Inf）或 NaN 偏移。负数是根据规范，
	 * 而 NaN 是因为相关的语义似乎不明显。
	 */
	if (NUMERIC_IS_NAN(fc_offset) ||
		NUMERIC_IS_NINF(fc_offset) ||
		NUMERIC_SIGN(fc_offset) == NUMERIC_NEG)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
				 errmsg("invalid preceding or following size in window function")));

	/*
	 * 处理 val 和/或 base 为 NaN 的情况，遵循 NaN 排在非 NaN 之后的规则
	 * （参见 cmp_numerics）。偏移量不能影响结论。
	 */
	if (NUMERIC_IS_NAN(fc_val))
	{
		if (NUMERIC_IS_NAN(fc_base))
			fc_result = true;		/* NAN = NAN */
		else
			fc_result = !fc_less;		/* NAN > 非NAN */
	}
	else if (NUMERIC_IS_NAN(fc_base))
	{
		fc_result = fc_less;			/* 非NAN < NAN */
	}

	/*
	 * 处理无限偏移（此时必然是 +Inf）。
	 */
	else if (NUMERIC_IS_SPECIAL(fc_offset))
	{
		Assert(NUMERIC_IS_PINF(fc_offset));
		if (fc_sub ? NUMERIC_IS_PINF(fc_base) : NUMERIC_IS_NINF(fc_base))
		{
			/*
			 * base +/- offset 将产生 NaN，因此对任何 val 返回 true
			 * （参见 in_range_float8_float8() 了解推理）。
			 */
			fc_result = true;
		}
		else if (fc_sub)
		{
			/* base - offset 必须是 -inf */
			if (fc_less)
				fc_result = NUMERIC_IS_NINF(fc_val);	/* 只有 -inf <= sum */
			else
				fc_result = true;	/* 任何 val >= sum */
		}
		else
		{
			/* base + offset 必须是 +inf */
			if (fc_less)
				fc_result = true;	/* 任何 val <= sum */
			else
				fc_result = NUMERIC_IS_PINF(fc_val);	/* 只有 +inf >= sum */
		}
	}

	/*
	 * 处理 val 和/或 base 为无穷大的情况。偏移量现在已知为有限的，
	 * 不能影响结论。
	 */
	else if (NUMERIC_IS_SPECIAL(fc_val))
	{
		if (NUMERIC_IS_PINF(fc_val))
		{
			if (NUMERIC_IS_PINF(fc_base))
				fc_result = true;	/* PINF = PINF */
			else
				fc_result = !fc_less; /* PINF > 任何其他非 NAN */
		}
		else					/* val 必须是 NINF */
		{
			if (NUMERIC_IS_NINF(fc_base))
				fc_result = true;	/* NINF = NINF */
			else
				fc_result = fc_less;	/* NINF < 其他任何值 */
		}
	}
	else if (NUMERIC_IS_SPECIAL(fc_base))
	{
		if (NUMERIC_IS_NINF(fc_base))
			fc_result = !fc_less;		/* normal > NINF */
		else
			fc_result = fc_less;		/* normal < PINF */
	}
	else
	{
		/*
		 * 否则继续计算 base +/- offset。虽然可能会
		 * 溢出数字格式，但这种情况不太可能到让我们采取措施防止它。
		 */
		NumericVar	fc_valv;
		NumericVar	fc_basev;
		NumericVar	fc_offsetv;
		NumericVar	fc_sum;

		fc_init_var_from_num(fc_val, &fc_valv);
		fc_init_var_from_num(fc_base, &fc_basev);
		fc_init_var_from_num(fc_offset, &fc_offsetv);
		init_var(&fc_sum);

		if (fc_sub)
			fc_sub_var(&fc_basev, &fc_offsetv, &fc_sum);
		else
			add_var(&fc_basev, &fc_offsetv, &fc_sum);

		if (fc_less)
			fc_result = (fc_cmp_var(&fc_valv, &fc_sum) <= 0);
		else
			fc_result = (fc_cmp_var(&fc_valv, &fc_sum) >= 0);

		fc_free_var(&fc_sum);
	}

	PG_FREE_IF_COPY(fc_val, 0);
	PG_FREE_IF_COPY(fc_base, 1);
	PG_FREE_IF_COPY(fc_offset, 2);

	PG_RETURN_BOOL(fc_result);
}

Datum hash_numeric(PG_FUNCTION_ARGS)
{
	Numeric		fc_key = PG_GETARG_NUMERIC(0);
	Datum		fc_digit_hash;
	Datum		fc_result;
	int			fc_weight;
	int			fc_start_offset;
	int			fc_end_offset;
	int			fc_i;
	int			fc_hash_len;
	NumericDigit *fc_digits;

	/* 如果是 NaN 或无穷大，不要尝试哈希其他字段 */
	if (NUMERIC_IS_SPECIAL(fc_key))
		PG_RETURN_UINT32(0);

	fc_weight = NUMERIC_WEIGHT(fc_key);
	fc_start_offset = 0;
	fc_end_offset = 0;

	/*
	 * 从输入哈希中省略任何前导或尾随零。数字实现 *应该* 保证前导和尾随
	 * 零被抑制，但我们是偏执的。注意，我们测量起始和结束偏移量时使用的是 NumericDigits 单位，而不是字节。
	 */
	fc_digits = NUMERIC_DIGITS(fc_key);
	for (fc_i = 0; fc_i < NUMERIC_NDIGITS(fc_key); fc_i++)
	{
		if (fc_digits[fc_i] != (NumericDigit) 0)
			break;

		fc_start_offset++;

		/*
		 * 权重实际上是小数点前的数字位数，因此每跳过一个前导零就递减一次。
		 */
		fc_weight--;
	}

	/*
	 * 如果没有非零数字，则该数字的值为零，
	 * 无论其他字段如何。
	 */
	if (NUMERIC_NDIGITS(fc_key) == fc_start_offset)
		PG_RETURN_UINT32(-1);

	for (fc_i = NUMERIC_NDIGITS(fc_key) - 1; fc_i >= 0; fc_i--)
	{
		if (fc_digits[fc_i] != (NumericDigit) 0)
			break;

		fc_end_offset++;
	}

	/* 如果走到这里，应该至少有一个非零数字 */
	Assert(fc_start_offset + fc_end_offset < NUMERIC_NDIGITS(fc_key));

	/*
	 * 注意，我们不针对 Numeric 的比例进行哈希，因为两个数字可以
	 * 比较相等但具有不同的比例。我们也不针对符号进行哈希，尽管我们可以：由于符号差异意味着不等式，
	 * 这不应影响正确性。
	 */
	fc_hash_len = NUMERIC_NDIGITS(fc_key) - fc_start_offset - fc_end_offset;
	fc_digit_hash = hash_any((unsigned char *) (NUMERIC_DIGITS(fc_key) + fc_start_offset),
						  fc_hash_len * sizeof(NumericDigit));

	/* 混入权重，通过 XOR */
	fc_result = fc_digit_hash ^ fc_weight;

	PG_RETURN_DATUM(fc_result);
}

/*
 * 通过将值哈希为 64 位值返回 64 位值，并使用种子。
 * 否则，类似于 hash_numeric。
 */
Datum hash_numeric_extended(PG_FUNCTION_ARGS)
{
	Numeric		fc_key = PG_GETARG_NUMERIC(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	Datum		fc_digit_hash;
	Datum		fc_result;
	int			fc_weight;
	int			fc_start_offset;
	int			fc_end_offset;
	int			fc_i;
	int			fc_hash_len;
	NumericDigit *fc_digits;

	/* 如果是 NaN 或无穷大，不要尝试哈希其他字段 */
	if (NUMERIC_IS_SPECIAL(fc_key))
		PG_RETURN_UINT64(fc_seed);

	fc_weight = NUMERIC_WEIGHT(fc_key);
	fc_start_offset = 0;
	fc_end_offset = 0;

	fc_digits = NUMERIC_DIGITS(fc_key);
	for (fc_i = 0; fc_i < NUMERIC_NDIGITS(fc_key); fc_i++)
	{
		if (fc_digits[fc_i] != (NumericDigit) 0)
			break;

		fc_start_offset++;

		fc_weight--;
	}

	if (NUMERIC_NDIGITS(fc_key) == fc_start_offset)
		PG_RETURN_UINT64(fc_seed - 1);

	for (fc_i = NUMERIC_NDIGITS(fc_key) - 1; fc_i >= 0; fc_i--)
	{
		if (fc_digits[fc_i] != (NumericDigit) 0)
			break;

		fc_end_offset++;
	}

	Assert(fc_start_offset + fc_end_offset < NUMERIC_NDIGITS(fc_key));

	fc_hash_len = NUMERIC_NDIGITS(fc_key) - fc_start_offset - fc_end_offset;
	fc_digit_hash = hash_any_extended((unsigned char *) (NUMERIC_DIGITS(fc_key)
													  + fc_start_offset),
								   fc_hash_len * sizeof(NumericDigit),
								   fc_seed);

	fc_result = UInt64GetDatum(DatumGetUInt64(fc_digit_hash) ^ fc_weight);

	PG_RETURN_DATUM(fc_result);
}


/* ----------------------------------------------------------------------
 *
 * 基本算术函数
 *
 * ----------------------------------------------------------------------
 */


/*
 * numeric_add() -
 *
 *	添加两个数字
 */
Datum numeric_add(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;

	fc_res = numeric_add_opt_error(fc_num1, fc_num2, NULL);

	PG_RETURN_NUMERIC(fc_res);
}

/*
 * numeric_add_opt_error() -
 *
 *	numeric_add() 的内部版本。如果提供了 "*have_error" 标志，发生错误时将其设置为 true，并返回 NULL。这对需要自行处理错误的调用者很有帮助。
 */
Numeric numeric_add_opt_error(Numeric fc_num1, Numeric fc_num2, bool *fc_have_error)
{
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			return fc_make_result(&const_nan);
		if (NUMERIC_IS_PINF(fc_num1))
		{
			if (NUMERIC_IS_NINF(fc_num2))
				return fc_make_result(&const_nan); /* Inf + -Inf */
			else
				return fc_make_result(&const_pinf);
		}
		if (NUMERIC_IS_NINF(fc_num1))
		{
			if (NUMERIC_IS_PINF(fc_num2))
				return fc_make_result(&const_nan); /* -Inf + Inf */
			else
				return fc_make_result(&const_ninf);
		}
		/* 到这里，num1 必须是有限的，因此 num2 不是 */
		if (NUMERIC_IS_PINF(fc_num2))
			return fc_make_result(&const_pinf);
		Assert(NUMERIC_IS_NINF(fc_num2));
		return fc_make_result(&const_ninf);
	}

	/*
	 * 解包值，让 add_var() 计算结果并返回。
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);
	add_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result_opt_error(&fc_result, fc_have_error);

	fc_free_var(&fc_result);

	return fc_res;
}


/*
 * numeric_sub() -
 *
 *	从一个数字中减去另一个数字
 */
Datum numeric_sub(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;

	fc_res = numeric_sub_opt_error(fc_num1, fc_num2, NULL);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_sub_opt_error() -
 *
 *	numeric_sub() 的内部版本。如果提供了 "*have_error" 标志，发生错误时将其设置为 true，并返回 NULL。这对需要自行处理错误的调用者很有帮助。
 */
Numeric numeric_sub_opt_error(Numeric fc_num1, Numeric fc_num2, bool *fc_have_error)
{
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			return fc_make_result(&const_nan);
		if (NUMERIC_IS_PINF(fc_num1))
		{
			if (NUMERIC_IS_PINF(fc_num2))
				return fc_make_result(&const_nan); /* Inf - Inf */
			else
				return fc_make_result(&const_pinf);
		}
		if (NUMERIC_IS_NINF(fc_num1))
		{
			if (NUMERIC_IS_NINF(fc_num2))
				return fc_make_result(&const_nan); /* -Inf - -Inf */
			else
				return fc_make_result(&const_ninf);
		}
		/* 到这里，num1 必须是有限的，因此 num2 不是 */
		if (NUMERIC_IS_PINF(fc_num2))
			return fc_make_result(&const_ninf);
		Assert(NUMERIC_IS_NINF(fc_num2));
		return fc_make_result(&const_pinf);
	}

	/*
	 * 解包值，让 sub_var() 计算结果并返回。
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);
	fc_sub_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result_opt_error(&fc_result, fc_have_error);

	fc_free_var(&fc_result);

	return fc_res;
}


/*
 * numeric_mul() -
 *
 *	计算两个数字的乘积
 */
Datum numeric_mul(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;

	fc_res = numeric_mul_opt_error(fc_num1, fc_num2, NULL);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_mul_opt_error() -
 *
 *	numeric_mul() 的内部版本。如果提供了 "*have_error" 标志，发生错误时将其设置为 true，并返回 NULL。这对需要自行处理错误的调用者很有帮助。
 */
Numeric numeric_mul_opt_error(Numeric fc_num1, Numeric fc_num2, bool *fc_have_error)
{
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			return fc_make_result(&const_nan);
		if (NUMERIC_IS_PINF(fc_num1))
		{
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					return fc_make_result(&const_nan); /* Inf * 0 */
				case 1:
					return fc_make_result(&const_pinf);
				case -1:
					return fc_make_result(&const_ninf);
			}
			Assert(false);
		}
		if (NUMERIC_IS_NINF(fc_num1))
		{
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					return fc_make_result(&const_nan); /* -Inf * 0 */
				case 1:
					return fc_make_result(&const_ninf);
				case -1:
					return fc_make_result(&const_pinf);
			}
			Assert(false);
		}
		/* 到这里，num1 必须是有限的，因此 num2 不是 */
		if (NUMERIC_IS_PINF(fc_num2))
		{
			switch (fc_numeric_sign_internal(fc_num1))
			{
				case 0:
					return fc_make_result(&const_nan); /* 0 * Inf */
				case 1:
					return fc_make_result(&const_pinf);
				case -1:
					return fc_make_result(&const_ninf);
			}
			Assert(false);
		}
		Assert(NUMERIC_IS_NINF(fc_num2));
		switch (fc_numeric_sign_internal(fc_num1))
		{
			case 0:
				return fc_make_result(&const_nan); /* 0 * -Inf */
			case 1:
				return fc_make_result(&const_ninf);
			case -1:
				return fc_make_result(&const_pinf);
		}
		Assert(false);
	}

	/*
	 * 解包值，让 mul_var() 计算结果并返回。与 add_var() 和 sub_var() 不同，mul_var() 将对其结果进行舍入。在 numeric_mul() 的情况下，该函数用于数字上的 * 运算符，我们请求乘积的精确表示（rscale = sum(dscale of arg1, dscale of arg2)）。如果精确结果的小数点后有比数字可以存储的更多的位数，则我们将其舍入。在计算精确结果后进行舍入，确保最终结果是正确舍入的（在 mul_var() 中使用截断的乘积进行舍入并不能保证这一点）。
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);
	fc_mul_var(&fc_arg1, &fc_arg2, &fc_result, fc_arg1.dscale + fc_arg2.dscale);

	if (fc_result.dscale > NUMERIC_DSCALE_MAX)
		fc_round_var(&fc_result, NUMERIC_DSCALE_MAX);

	fc_res = fc_make_result_opt_error(&fc_result, fc_have_error);

	fc_free_var(&fc_result);

	return fc_res;
}


/*
 * numeric_div() -
 *
 *	将一个数字除以另一个数字
 */
Datum numeric_div(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;

	fc_res = numeric_div_opt_error(fc_num1, fc_num2, NULL);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_div_opt_error() -
 *
 *	numeric_div() 的内部版本。如果提供了 "*have_error" 标志，发生错误时将其设置为 true，并返回 NULL。这对需要自行处理错误的调用者很有帮助。
 */
Numeric numeric_div_opt_error(Numeric fc_num1, Numeric fc_num2, bool *fc_have_error)
{
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;
	int			fc_rscale;

	if (fc_have_error)
		*fc_have_error = false;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			return fc_make_result(&const_nan);
		if (NUMERIC_IS_PINF(fc_num1))
		{
			if (NUMERIC_IS_SPECIAL(fc_num2))
				return fc_make_result(&const_nan); /* Inf / [-]Inf */
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					if (fc_have_error)
					{
						*fc_have_error = true;
						return NULL;
					}
					ereport(ERROR,
							(errcode(ERRCODE_DIVISION_BY_ZERO),
							 errmsg("division by zero")));
					break;
				case 1:
					return fc_make_result(&const_pinf);
				case -1:
					return fc_make_result(&const_ninf);
			}
			Assert(false);
		}
		if (NUMERIC_IS_NINF(fc_num1))
		{
			if (NUMERIC_IS_SPECIAL(fc_num2))
				return fc_make_result(&const_nan); /* -Inf / [-]Inf */
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					if (fc_have_error)
					{
						*fc_have_error = true;
						return NULL;
					}
					ereport(ERROR,
							(errcode(ERRCODE_DIVISION_BY_ZERO),
							 errmsg("division by zero")));
					break;
				case 1:
					return fc_make_result(&const_ninf);
				case -1:
					return fc_make_result(&const_pinf);
			}
			Assert(false);
		}
		/* 到这里，num1 必须是有限的，因此 num2 不是 */

		/*
		 * POSIX 规定如果 num1 为零，则返回零或负零，否则抛出下溢错误。但数字类型实际上不执行下溢，因此我们只返回零。
		 */
		return fc_make_result(&const_zero);
	}

	/*
	 * 解包参数
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);

	/*
	 * 为除法结果选择比例
	 */
	fc_rscale = fc_select_div_scale(&fc_arg1, &fc_arg2);

	/*
	 * 如果提供了 "have_error"，在此检查除以零
	 */
	if (fc_have_error && (fc_arg2.ndigits == 0 || fc_arg2.digits[0] == 0))
	{
		*fc_have_error = true;
		return NULL;
	}

	/*
	 * 进行除法并返回结果
	 */
	fc_div_var(&fc_arg1, &fc_arg2, &fc_result, fc_rscale, true);

	fc_res = fc_make_result_opt_error(&fc_result, fc_have_error);

	fc_free_var(&fc_result);

	return fc_res;
}


/*
 * numeric_div_trunc() -
 *
 *	将一个数字除以另一个数字，结果截断为整数
 */
Datum numeric_div_trunc(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			PG_RETURN_NUMERIC(fc_make_result(&const_nan));
		if (NUMERIC_IS_PINF(fc_num1))
		{
			if (NUMERIC_IS_SPECIAL(fc_num2))
				PG_RETURN_NUMERIC(fc_make_result(&const_nan)); /* Inf / [-]Inf */
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					ereport(ERROR,
							(errcode(ERRCODE_DIVISION_BY_ZERO),
							 errmsg("division by zero")));
					break;
				case 1:
					PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
				case -1:
					PG_RETURN_NUMERIC(fc_make_result(&const_ninf));
			}
			Assert(false);
		}
		if (NUMERIC_IS_NINF(fc_num1))
		{
			if (NUMERIC_IS_SPECIAL(fc_num2))
				PG_RETURN_NUMERIC(fc_make_result(&const_nan)); /* -Inf / [-]Inf */
			switch (fc_numeric_sign_internal(fc_num2))
			{
				case 0:
					ereport(ERROR,
							(errcode(ERRCODE_DIVISION_BY_ZERO),
							 errmsg("division by zero")));
					break;
				case 1:
					PG_RETURN_NUMERIC(fc_make_result(&const_ninf));
				case -1:
					PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
			}
			Assert(false);
		}
		/* 到这里，num1 必须是有限的，因此 num2 不是 */

		/*
		 * POSIX 规定如果 num1 为零，则返回零或负零，否则抛出下溢错误。但数字类型实际上不执行下溢，因此我们只返回零。
		 */
		PG_RETURN_NUMERIC(fc_make_result(&const_zero));
	}

	/*
	 * 解包参数
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);

	/*
	 * 进行除法并返回结果
	 */
	fc_div_var(&fc_arg1, &fc_arg2, &fc_result, 0, false);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_mod() -
 *
 *	计算两个数字的模
 */
Datum numeric_mod(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;

	fc_res = numeric_mod_opt_error(fc_num1, fc_num2, NULL);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_mod_opt_error() -
 *
 *	numeric_mod() 的内部版本。如果提供了 "*have_error" 标志，发生错误时将其设置为 true，并返回 NULL。这对需要自行处理错误的调用者很有帮助。
 */
Numeric numeric_mod_opt_error(Numeric fc_num1, Numeric fc_num2, bool *fc_have_error)
{
	Numeric		fc_res;
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;

	if (fc_have_error)
		*fc_have_error = false;

	/*
	 * 处理 NaN 和无穷大。我们遵循 POSIX fmod()，除了 POSIX 将 x-is-infinite 和 y-is-zero 相同处理，抛出 EDOM 并返回 NaN。我们选择仅在 y-is-zero 时抛出错误。
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			return fc_make_result(&const_nan);
		if (NUMERIC_IS_INF(fc_num1))
		{
			if (fc_numeric_sign_internal(fc_num2) == 0)
			{
				if (fc_have_error)
				{
					*fc_have_error = true;
					return NULL;
				}
				ereport(ERROR,
						(errcode(ERRCODE_DIVISION_BY_ZERO),
						 errmsg("division by zero")));
			}
			/* Inf % any nonzero = NaN */
			return fc_make_result(&const_nan);
		}
		/* num2 必须是 [-]Inf；结果是 num1，无论 num2 的符号如何 */
		return fc_duplicate_numeric(fc_num1);
	}

	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);

	/*
	 * 如果提供了 "have_error"，在此检查除以零
	 */
	if (fc_have_error && (fc_arg2.ndigits == 0 || fc_arg2.digits[0] == 0))
	{
		*fc_have_error = true;
		return NULL;
	}

	fc_mod_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result_opt_error(&fc_result, NULL);

	fc_free_var(&fc_result);

	return fc_res;
}


/*
 * numeric_inc() -
 *
 *	将数字增加 one
 */
Datum numeric_inc(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_arg;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	/*
	 * 计算结果并返回
	 */
	fc_init_var_from_num(fc_num, &fc_arg);

	add_var(&fc_arg, &const_one, &fc_arg);

	fc_res = fc_make_result(&fc_arg);

	fc_free_var(&fc_arg);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_smaller() -
 *
 *	返回两个数字中较小的一个
 */
Datum numeric_smaller(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);

	/*
	 * 使用 cmp_numerics 以使其与比较操作符一致，
	 * 特别是在涉及 NaN 的比较时。
	 */
	if (fc_cmp_numerics(fc_num1, fc_num2) < 0)
		PG_RETURN_NUMERIC(fc_num1);
	else
		PG_RETURN_NUMERIC(fc_num2);
}


/*
 * numeric_larger() -
 *
 *	返回两个数字中较大的一个
 */
Datum numeric_larger(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);

	/*
	 * 使用 cmp_numerics 以使其与比较操作符一致，
	 * 特别是在涉及 NaN 的比较时。
	 */
	if (fc_cmp_numerics(fc_num1, fc_num2) > 0)
		PG_RETURN_NUMERIC(fc_num1);
	else
		PG_RETURN_NUMERIC(fc_num2);
}


/* ----------------------------------------------------------------------
 *
 * 高级数学函数
 *
 * ----------------------------------------------------------------------
 */

/*
 * numeric_gcd() -
 *
 *	计算两个数字的最大公约数
 */
Datum numeric_gcd(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大：我们在所有这些情况下将结果视为 NaN。
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	/*
	 * 解包参数
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);

	/*
	 * 找出最大公约数并返回结果
	 */
	fc_gcd_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_lcm() -
 *
 *	计算两个数字的最小公倍数
	 */
Datum numeric_lcm(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	Numeric		fc_res;

	/*
	 * 处理 NaN 和无穷大：我们在所有这些情况下将结果视为 NaN。
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	/*
	 * 解包参数
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	init_var(&fc_result);

	/*
	 * 使用 lcm(x, y) = abs(x / gcd(x, y) * y) 计算结果，
	 * 如果任一输入为零，则返回零。
	 *
	 * 注意，除法保证是精确的，返回一个整数结果，
	 * 因此最小公倍数是 x 和 y 的整数倍。显示
	 * 尺寸为 Min(x.dscale, y.dscale) 就足以表示它，
	 * 但是与其他数值函数一样，我们选择返回一个
	 * 显示尺寸不小于任一输入的结果。
	 */
	if (fc_arg1.ndigits == 0 || fc_arg2.ndigits == 0)
		set_var_from_var(&const_zero, &fc_result);
	else
	{
		fc_gcd_var(&fc_arg1, &fc_arg2, &fc_result);
		fc_div_var(&fc_arg1, &fc_result, &fc_result, 0, false);
		fc_mul_var(&fc_arg2, &fc_result, &fc_result, fc_arg2.dscale);
		fc_result.sign = NUMERIC_POS;
	}

	fc_result.dscale = Max(fc_arg1.dscale, fc_arg2.dscale);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_fac()
 *
 * 计算阶乘
	 */
Datum numeric_fac(PG_FUNCTION_ARGS)
{
	int64		fc_num = PG_GETARG_INT64(0);
	Numeric		fc_res;
	NumericVar	fc_fact;
	NumericVar	fc_result;

	if (fc_num < 0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("factorial of a negative number is undefined")));
	if (fc_num <= 1)
	{
		fc_res = fc_make_result(&const_one);
		PG_RETURN_NUMERIC(fc_res);
	}
	/* 如果结果会溢出则立即失败 */
	if (fc_num > 32177)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value overflows numeric format")));

	init_var(&fc_fact);
	init_var(&fc_result);

	fc_int64_to_numericvar(fc_num, &fc_result);

	for (fc_num = fc_num - 1; fc_num > 1; fc_num--)
	{
		/* 这个循环可能会花费一段时间，因此允许中断 */
		CHECK_FOR_INTERRUPTS();

		fc_int64_to_numericvar(fc_num, &fc_fact);

		fc_mul_var(&fc_result, &fc_fact, &fc_result, 0);
	}

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_fact);
	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_sqrt() -
 *
 *	计算一个数字的平方根。
	 */
Datum numeric_sqrt(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_arg;
	NumericVar	fc_result;
	int			fc_sweight;
	int			fc_rscale;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		/* 错误应与 sqrt_var() 中的匹配 */
		if (NUMERIC_IS_NINF(fc_num))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
					 errmsg("cannot take square root of a negative number")));
		/* 对于 NAN 或 PINF，只需重复输入 */
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));
	}

	/*
	 * 解包参数并确定结果的规模。我们选择一个规模
	 * 至少提供 NUMERIC_MIN_SIG_DIGITS 个有效数字；但在任何
	 * 情况下不低于输入的 dscale。
	 */
	fc_init_var_from_num(fc_num, &fc_arg);

	init_var(&fc_result);

	/* 假设输入已标准化，因此 arg.weight 是准确的 */
	fc_sweight = (fc_arg.weight + 1) * DEC_DIGITS / 2 - 1;

	fc_rscale = NUMERIC_MIN_SIG_DIGITS - fc_sweight;
	fc_rscale = Max(fc_rscale, fc_arg.dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	/*
	 * 让 sqrt_var() 进行计算并返回结果。
	 */
	fc_sqrt_var(&fc_arg, &fc_result, fc_rscale);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_exp() -
 *
 *	将 e 乘以 x 的幂
	 */
Datum numeric_exp(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_arg;
	NumericVar	fc_result;
	int			fc_rscale;
	double		fc_val;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		/* 根据 POSIX，exp(-Inf) 为零 */
		if (NUMERIC_IS_NINF(fc_num))
			PG_RETURN_NUMERIC(fc_make_result(&const_zero));
		/* 对于 NAN 或 PINF，只需重复输入 */
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));
	}

	/*
	 * 解包参数并确定结果的规模。我们选择一个规模
	 * 至少提供 NUMERIC_MIN_SIG_DIGITS 个有效数字；但在任何
	 * 情况下不低于输入的 dscale。
	 */
	fc_init_var_from_num(fc_num, &fc_arg);

	init_var(&fc_result);

	/* 将输入转换为 float8，忽略溢出 */
	fc_val = fc_numericvar_to_double_no_overflow(&fc_arg);

	/*
	 * log10(result) = num * log10(e)，因此这是结果的大致十进制
	 * 权重：
	 */
	fc_val *= 0.434294481903252;

	/* 限制在不会导致整数溢出的范围内 */
	fc_val = Max(fc_val, -NUMERIC_MAX_RESULT_SCALE);
	fc_val = Min(fc_val, NUMERIC_MAX_RESULT_SCALE);

	fc_rscale = NUMERIC_MIN_SIG_DIGITS - (int) fc_val;
	fc_rscale = Max(fc_rscale, fc_arg.dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	/*
	 * 让 exp_var() 进行计算并返回结果。
	 */
	fc_exp_var(&fc_arg, &fc_result, fc_rscale);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_ln() -
 *
 *	计算 x 的自然对数
	 */
Datum numeric_ln(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_arg;
	NumericVar	fc_result;
	int			fc_ln_dweight;
	int			fc_rscale;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_NINF(fc_num))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
					 errmsg("cannot take logarithm of a negative number")));
		/* 对于 NAN 或 PINF，只需重复输入 */
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));
	}

	fc_init_var_from_num(fc_num, &fc_arg);
	init_var(&fc_result);

	/* 对数的估计 dweight */
	fc_ln_dweight = fc_estimate_ln_dweight(&fc_arg);

	fc_rscale = NUMERIC_MIN_SIG_DIGITS - fc_ln_dweight;
	fc_rscale = Max(fc_rscale, fc_arg.dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	fc_ln_var(&fc_arg, &fc_result, fc_rscale);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_log() -
 *
 *	计算 x 在给定基数下的对数
	 */
Datum numeric_log(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		int			fc_sign1,
					fc_sign2;

		if (NUMERIC_IS_NAN(fc_num1) || NUMERIC_IS_NAN(fc_num2))
			PG_RETURN_NUMERIC(fc_make_result(&const_nan));
		/* 在负输入（包括 -Inf）上失败，如同 log_var 会那样 */
		fc_sign1 = fc_numeric_sign_internal(fc_num1);
		fc_sign2 = fc_numeric_sign_internal(fc_num2);
		if (fc_sign1 < 0 || fc_sign2 < 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
					 errmsg("cannot take logarithm of a negative number")));
		/* 在零输入上失败，如同 log_var 会那样 */
		if (fc_sign1 == 0 || fc_sign2 == 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
					 errmsg("cannot take logarithm of zero")));
		if (NUMERIC_IS_PINF(fc_num1))
		{
			/* log(Inf, Inf) 简化为 Inf/Inf，因此是 NaN */
			if (NUMERIC_IS_PINF(fc_num2))
				PG_RETURN_NUMERIC(fc_make_result(&const_nan));
			/* log(Inf, finite-positive) 为零（我们不抛出下溢） */
			PG_RETURN_NUMERIC(fc_make_result(&const_zero));
		}
		Assert(NUMERIC_IS_PINF(fc_num2));
		/* log(finite-positive, Inf) 为 Inf */
		PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	}

	/*
	 * 初始化
	 */
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);
	init_var(&fc_result);

	/*
	 * 调用 log_var() 计算并返回结果；注意它自行处理尺度选择。
	 */
	fc_log_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/*
 * numeric_power() -
 *
 *	将 x 提升到 y 的幂
 */
Datum numeric_power(PG_FUNCTION_ARGS)
{
	Numeric		fc_num1 = PG_GETARG_NUMERIC(0);
	Numeric		fc_num2 = PG_GETARG_NUMERIC(1);
	Numeric		fc_res;
	NumericVar	fc_arg1;
	NumericVar	fc_arg2;
	NumericVar	fc_result;
	int			fc_sign1,
				fc_sign2;

	/*
	 * 处理 NaN 和无穷大
	 */
	if (NUMERIC_IS_SPECIAL(fc_num1) || NUMERIC_IS_SPECIAL(fc_num2))
	{
		/*
		 * 我们遵循 pow(3) 的 POSIX 规范，该规范指出 NaN ^ 0 = 1，
		 * 且 1 ^ NaN = 1，而所有其他 NaN 输入的情况产生 NaN
		 * （没有错误）。
		 */
		if (NUMERIC_IS_NAN(fc_num1))
		{
			if (!NUMERIC_IS_SPECIAL(fc_num2))
			{
				fc_init_var_from_num(fc_num2, &fc_arg2);
				if (fc_cmp_var(&fc_arg2, &const_zero) == 0)
					PG_RETURN_NUMERIC(fc_make_result(&const_one));
			}
			PG_RETURN_NUMERIC(fc_make_result(&const_nan));
		}
		if (NUMERIC_IS_NAN(fc_num2))
		{
			if (!NUMERIC_IS_SPECIAL(fc_num1))
			{
				fc_init_var_from_num(fc_num1, &fc_arg1);
				if (fc_cmp_var(&fc_arg1, &const_one) == 0)
					PG_RETURN_NUMERIC(fc_make_result(&const_one));
			}
			PG_RETURN_NUMERIC(fc_make_result(&const_nan));
		}
		/* 至少有一个输入是无限的，但错误规则仍然适用 */
		fc_sign1 = fc_numeric_sign_internal(fc_num1);
		fc_sign2 = fc_numeric_sign_internal(fc_num2);
		if (fc_sign1 == 0 && fc_sign2 < 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
					 errmsg("zero raised to a negative power is undefined")));
		if (fc_sign1 < 0 && !fc_numeric_is_integral(fc_num2))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
					 errmsg("a negative number raised to a non-integer power yields a complex result")));

		/*
		 * POSIX 为 pow(3) 的无限输入提供了一系列规则：
		 *
		 * 对于任何 y 值，如果 x 为 +1，则返回 1.0。
		 */
		if (!NUMERIC_IS_SPECIAL(fc_num1))
		{
			fc_init_var_from_num(fc_num1, &fc_arg1);
			if (fc_cmp_var(&fc_arg1, &const_one) == 0)
				PG_RETURN_NUMERIC(fc_make_result(&const_one));
		}

		/*
		 * 对于任何 x 值，如果 y 为 [-]0，则返回 1.0。
		 */
		if (fc_sign2 == 0)
			PG_RETURN_NUMERIC(fc_make_result(&const_one));

		/*
		 * 对于任何大于 0 的奇数整数值 y，如果 x 为 [-]0，返回 [-]0。
		 * 对于 y > 0 且不是奇数整数的情况，如果 x 为 [-]0，返回 +0。
		 * （由于我们不处理负零，因此不需要区分这两种情况。）
		 */
		if (fc_sign1 == 0 && fc_sign2 > 0)
			PG_RETURN_NUMERIC(fc_make_result(&const_zero));

		/*
		 * 如果 x 为 -1，且 y 为 [-]Inf，则返回 1.0。
		 *
		 * 对于 |x| < 1，如果 y 为 -Inf，则返回 +Inf。
		 *
		 * 对于 |x| > 1，如果 y 为 -Inf，则返回 +0。
		 *
		 * 对于 |x| < 1，如果 y 为 +Inf，则返回 +0。
		 *
		 * 对于 |x| > 1，如果 y 为 +Inf，则返回 +Inf。
		 */
		if (NUMERIC_IS_INF(fc_num2))
		{
			bool		fc_abs_x_gt_one;

			if (NUMERIC_IS_SPECIAL(fc_num1))
				fc_abs_x_gt_one = true;	/* x 是 Inf 或 -Inf */
			else
			{
				fc_init_var_from_num(fc_num1, &fc_arg1);
				if (fc_cmp_var(&fc_arg1, &const_minus_one) == 0)
					PG_RETURN_NUMERIC(fc_make_result(&const_one));
				fc_arg1.sign = NUMERIC_POS;	/* 现在 arg1 = abs(x) */
				fc_abs_x_gt_one = (fc_cmp_var(&fc_arg1, &const_one) > 0);
			}
			if (fc_abs_x_gt_one == (fc_sign2 > 0))
				PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
			else
				PG_RETURN_NUMERIC(fc_make_result(&const_zero));
		}

		/*
		 * 对于 y < 0，如果 x 为 +Inf，则返回 +0。
		 *
		 * 对于 y > 0，如果 x 为 +Inf，则返回 +Inf。
		 */
		if (NUMERIC_IS_PINF(fc_num1))
		{
			if (fc_sign2 > 0)
				PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
			else
				PG_RETURN_NUMERIC(fc_make_result(&const_zero));
		}

		Assert(NUMERIC_IS_NINF(fc_num1));

		/*
		 * 对于 y 是小于 0 的奇数整数，如果 x 为 -Inf，返回 -0。
		 * 对于 y < 0 且不是奇数整数的情况，如果 x 为 -Inf，返回 +0。
		 * （再次，我们不需要区分这两种情况。）
		 */
		if (fc_sign2 < 0)
			PG_RETURN_NUMERIC(fc_make_result(&const_zero));

		/*
		 * 对于 y 是大于 0 的奇数整数，如果 x 为 -Inf，返回 -Inf。对于
		 * y > 0 且不是奇数整数的情况，如果 x 为 -Inf，返回 +Inf。
		 */
		fc_init_var_from_num(fc_num2, &fc_arg2);
		if (fc_arg2.ndigits > 0 && fc_arg2.ndigits == fc_arg2.weight + 1 &&
			(fc_arg2.digits[fc_arg2.ndigits - 1] & 1))
			PG_RETURN_NUMERIC(fc_make_result(&const_ninf));
		else
			PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	}

	/*
	 * SQL 规范要求我们在某些错误条件下发出特定的 SQLSTATE 错误代码。
	 * 具体而言，我们不返回 0 ^ -1 的除以零错误代码。
	 * 将负数提升到非整数的幂必须产生相同的错误代码，但该情况在 power_var() 中处理。
	 */
	fc_sign1 = fc_numeric_sign_internal(fc_num1);
	fc_sign2 = fc_numeric_sign_internal(fc_num2);

	if (fc_sign1 == 0 && fc_sign2 < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
				 errmsg("zero raised to a negative power is undefined")));

	/*
	 * 初始化
	 */
	init_var(&fc_result);
	fc_init_var_from_num(fc_num1, &fc_arg1);
	fc_init_var_from_num(fc_num2, &fc_arg2);

	/*
	 * 调用 power_var() 计算并返回结果；注意它自行处理尺度选择。
	 */
	fc_power_var(&fc_arg1, &fc_arg2, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}

/*
 * numeric_scale() -
 *
 *	返回比例，即小数部分的数字位数
 */
Datum numeric_scale(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);

	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NULL();

	PG_RETURN_INT32(NUMERIC_DSCALE(fc_num));
}

/*
 * 计算值的最小比例。
 */
static int get_min_scale(NumericVar *fc_var)
{
	int			fc_min_scale;
	int			fc_last_digit_pos;

	/*
	 * 通常，输入值将被“剥离”，以便最后一个 NumericDigit 不为零。
	 * 但如果不是，我们不想进入无限循环，因此明确找到最后一个非零数字。
	 */
	fc_last_digit_pos = fc_var->ndigits - 1;
	while (fc_last_digit_pos >= 0 &&
		   fc_var->digits[fc_last_digit_pos] == 0)
		fc_last_digit_pos--;

	if (fc_last_digit_pos >= 0)
	{
		/* 假设最后一个数字位没有零，计算 min_scale */
		fc_min_scale = (fc_last_digit_pos - fc_var->weight) * DEC_DIGITS;

		/*
		 * 如果小数点后没有数字，我们可能会得到负结果。
		 * 在这种情况下，min_scale 必须为零。
		 */
		if (fc_min_scale > 0)
		{
			/*
			 * 如果最后一个 NumericDigit 中的尾随数字为零，则减少 min_scale。
			 */
			NumericDigit fc_last_digit = fc_var->digits[fc_last_digit_pos];

			while (fc_last_digit % 10 == 0)
			{
				fc_min_scale--;
				fc_last_digit /= 10;
			}
		}
		else
			fc_min_scale = 0;
	}
	else
		fc_min_scale = 0;			/* 如果输入为零的结果 */

	return fc_min_scale;
}

/*
 * 返回所需的最小比例，以在不损失的情况下表示提供的值。
 */
Datum numeric_min_scale(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_arg;
	int			fc_min_scale;

	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NULL();

	fc_init_var_from_num(fc_num, &fc_arg);
	fc_min_scale = get_min_scale(&fc_arg);
	fc_free_var(&fc_arg);

	PG_RETURN_INT32(fc_min_scale);
}

/*
 * 减少数值的比例，以在不损失的情况下表示提供的值。
 */
Datum numeric_trim_scale(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	Numeric		fc_res;
	NumericVar	fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
		PG_RETURN_NUMERIC(fc_duplicate_numeric(fc_num));

	fc_init_var_from_num(fc_num, &fc_result);
	fc_result.dscale = get_min_scale(&fc_result);
	fc_res = fc_make_result(&fc_result);
	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


/* ----------------------------------------------------------------------
 *
 * 类型转换函数
 *
 * ----------------------------------------------------------------------
 */

Numeric int64_to_numeric(int64 fc_val)
{
	Numeric		fc_res;
	NumericVar	fc_result;

	init_var(&fc_result);

	fc_int64_to_numericvar(fc_val, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	return fc_res;
}

/*
 * 将 val1/(10**log10val2) 转换为数字。这比正常的
 * 数值除法快得多。
 */
Numeric int64_div_fast_to_numeric(int64 fc_val1, int fc_log10val2)
{
	Numeric		fc_res;
	NumericVar	fc_result;
	int			fc_rscale;
	int			fc_w;
	int			fc_m;

	init_var(&fc_result);

	/* 结果刻度 */
	fc_rscale = fc_log10val2 < 0 ? 0 : fc_log10val2;

	/* 减少权重的数量 */
	fc_w = fc_log10val2 / DEC_DIGITS;
	/* 除法剩余的数量 */
	fc_m = fc_log10val2 % DEC_DIGITS;
	if (fc_m < 0)
	{
		fc_m += DEC_DIGITS;
		fc_w--;
	}

	/*
	 * 如果仍有剩余量可以除以（10^m，0 < m < DEC_DIGITS），
	 * 将被除数乘以10^(DEC_DIGITS - m)，并再移动权重一次。
	 */
	if (fc_m > 0)
	{
#if DEC_DIGITS == 4
		static const int fc_pow10[] = {1, 10, 100, 1000};
#elif DEC_DIGITS == 2
		static const int pow10[] = {1, 10};
#elif DEC_DIGITS == 1
		static const int pow10[] = {1};
#else
#error unsupported NBASE
#endif
		int64		fc_factor = fc_pow10[DEC_DIGITS - fc_m];
		int64		fc_new_val1;

		StaticAssertStmt(lengthof(fc_pow10) == DEC_DIGITS, "mismatch with DEC_DIGITS");

		if (unlikely(pg_mul_s64_overflow(fc_val1, fc_factor, &fc_new_val1)))
		{
#ifdef HAVE_INT128
			/* 使用128位整数进行乘法计算 */
			int128		fc_tmp;

			fc_tmp = (int128) fc_val1 * (int128) fc_factor;

			fc_int128_to_numericvar(fc_tmp, &fc_result);
#else
			/* 使用数字进行乘法计算 */
			NumericVar	fc_tmp;

			init_var(&fc_tmp);

			int64_to_numericvar(val1, &result);
			int64_to_numericvar(fc_factor, &fc_tmp);
			mul_var(&result, &fc_tmp, &result, 0);

			free_var(&fc_tmp);
#endif
		}
		else
			fc_int64_to_numericvar(fc_new_val1, &fc_result);

		fc_w++;
	}
	else
		fc_int64_to_numericvar(fc_val1, &fc_result);

	fc_result.weight -= fc_w;
	fc_result.dscale = fc_rscale;

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	return fc_res;
}

Datum int4_numeric(PG_FUNCTION_ARGS)
{
	int32		fc_val = PG_GETARG_INT32(0);

	PG_RETURN_NUMERIC(int64_to_numeric(fc_val));
}

int32 numeric_int4_opt_error(Numeric fc_num, bool *fc_have_error)
{
	NumericVar	fc_x;
	int32		fc_result;

	if (fc_have_error)
		*fc_have_error = false;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (fc_have_error)
		{
			*fc_have_error = true;
			return 0;
		}
		else
		{
			if (NUMERIC_IS_NAN(fc_num))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot convert NaN to %s", "integer")));
			else
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot convert infinity to %s", "integer")));
		}
	}

	/* 转换为变量格式，然后转换为int4 */
	fc_init_var_from_num(fc_num, &fc_x);

	if (!fc_numericvar_to_int32(&fc_x, &fc_result))
	{
		if (fc_have_error)
		{
			*fc_have_error = true;
			return 0;
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("integer out of range")));
		}
	}

	return fc_result;
}

Datum numeric_int4(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);

	PG_RETURN_INT32(numeric_int4_opt_error(fc_num, NULL));
}

/*
 * 给定一个NumericVar，将其转换为int32。如果NumericVar
 * 超出了int32的范围，将返回false，否则返回true。
 * 输入的NumericVar不会被释放。
 */
static bool fc_numericvar_to_int32(const NumericVar *fc_var, int32 *fc_result)
{
	int64		fc_val;

	if (!fc_numericvar_to_int64(fc_var, &fc_val))
		return false;

	if (unlikely(fc_val < PG_INT32_MIN) || unlikely(fc_val > PG_INT32_MAX))
		return false;

	/* 向下转换为int4 */
	*fc_result = (int32) fc_val;

	return true;
}

Datum int8_numeric(PG_FUNCTION_ARGS)
{
	int64		fc_val = PG_GETARG_INT64(0);

	PG_RETURN_NUMERIC(int64_to_numeric(fc_val));
}


Datum numeric_int8(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_x;
	int64		fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_NAN(fc_num))
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert NaN to %s", "bigint")));
		else
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert infinity to %s", "bigint")));
	}

	/* 转换为变量格式，然后转换为int8 */
	fc_init_var_from_num(fc_num, &fc_x);

	if (!fc_numericvar_to_int64(&fc_x, &fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("bigint out of range")));

	PG_RETURN_INT64(fc_result);
}


Datum int2_numeric(PG_FUNCTION_ARGS)
{
	int16		fc_val = PG_GETARG_INT16(0);

	PG_RETURN_NUMERIC(int64_to_numeric(fc_val));
}


Datum numeric_int2(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_x;
	int64		fc_val;
	int16		fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_NAN(fc_num))
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert NaN to %s", "smallint")));
		else
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert infinity to %s", "smallint")));
	}

	/* 转换为变量格式，然后转换为int8 */
	fc_init_var_from_num(fc_num, &fc_x);

	if (!fc_numericvar_to_int64(&fc_x, &fc_val))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("smallint out of range")));

	if (unlikely(fc_val < PG_INT16_MIN) || unlikely(fc_val > PG_INT16_MAX))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("smallint out of range")));

	/* 向下转换为int2 */
	fc_result = (int16) fc_val;

	PG_RETURN_INT16(fc_result);
}


Datum float8_numeric(PG_FUNCTION_ARGS)
{
	float8		fc_val = PG_GETARG_FLOAT8(0);
	Numeric		fc_res;
	NumericVar	fc_result;
	char		fc_buf[DBL_DIG + 100];

	if (isnan(fc_val))
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	if (isinf(fc_val))
	{
		if (fc_val < 0)
			PG_RETURN_NUMERIC(fc_make_result(&const_ninf));
		else
			PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	}

	snprintf(fc_buf, sizeof(fc_buf), "%.*g", DBL_DIG, fc_val);

	init_var(&fc_result);

	/* 假设我们无需担心前导/后续空格 */
	(void) set_var_from_str(fc_buf, fc_buf, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


Datum numeric_float8(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	char	   *fc_tmp;
	Datum		fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			PG_RETURN_FLOAT8(get_float8_infinity());
		else if (NUMERIC_IS_NINF(fc_num))
			PG_RETURN_FLOAT8(-get_float8_infinity());
		else
			PG_RETURN_FLOAT8(get_float8_nan());
	}

	fc_tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
											  NumericGetDatum(fc_num)));

	fc_result = DirectFunctionCall1(float8in, CStringGetDatum(fc_tmp));

	pfree(fc_tmp);

	PG_RETURN_DATUM(fc_result);
}


/*
 * 将数字转换为float8；如果超出范围，返回+/- HUGE_VAL
 *
 * （内部辅助函数，不能直接从SQL调用）
 */
Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	double		fc_val;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			fc_val = HUGE_VAL;
		else if (NUMERIC_IS_NINF(fc_num))
			fc_val = -HUGE_VAL;
		else
			fc_val = get_float8_nan();
	}
	else
	{
		NumericVar	fc_x;

		fc_init_var_from_num(fc_num, &fc_x);
		fc_val = fc_numericvar_to_double_no_overflow(&fc_x);
	}

	PG_RETURN_FLOAT8(fc_val);
}

Datum float4_numeric(PG_FUNCTION_ARGS)
{
	float4		fc_val = PG_GETARG_FLOAT4(0);
	Numeric		fc_res;
	NumericVar	fc_result;
	char		fc_buf[FLT_DIG + 100];

	if (isnan(fc_val))
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	if (isinf(fc_val))
	{
		if (fc_val < 0)
			PG_RETURN_NUMERIC(fc_make_result(&const_ninf));
		else
			PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	}

	snprintf(fc_buf, sizeof(fc_buf), "%.*g", FLT_DIG, fc_val);

	init_var(&fc_result);

	/* 假设我们无需担心前导/后续空格 */
	(void) set_var_from_str(fc_buf, fc_buf, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
}


Datum numeric_float4(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	char	   *fc_tmp;
	Datum		fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_PINF(fc_num))
			PG_RETURN_FLOAT4(get_float4_infinity());
		else if (NUMERIC_IS_NINF(fc_num))
			PG_RETURN_FLOAT4(-get_float4_infinity());
		else
			PG_RETURN_FLOAT4(get_float4_nan());
	}

	fc_tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
											  NumericGetDatum(fc_num)));

	fc_result = DirectFunctionCall1(float4in, CStringGetDatum(fc_tmp));

	pfree(fc_tmp);

	PG_RETURN_DATUM(fc_result);
}


Datum numeric_pg_lsn(PG_FUNCTION_ARGS)
{
	Numeric		fc_num = PG_GETARG_NUMERIC(0);
	NumericVar	fc_x;
	XLogRecPtr	fc_result;

	if (NUMERIC_IS_SPECIAL(fc_num))
	{
		if (NUMERIC_IS_NAN(fc_num))
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert NaN to %s", "pg_lsn")));
		else
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot convert infinity to %s", "pg_lsn")));
	}

	/* 转换为变量格式，然后转换为pg_lsn */
	fc_init_var_from_num(fc_num, &fc_x);

	if (!fc_numericvar_to_uint64(&fc_x, (uint64 *) &fc_result))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("pg_lsn out of range")));

	PG_RETURN_LSN(fc_result);
}


/* ----------------------------------------------------------------------
 *
 * 聚合函数
 *
 * 所有这些聚合的过渡数据类型声明为INTERNAL。
 * 实际上，它是指向在聚合上下文中分配的NumericAggState的指针。
 * NumericVars的数字缓冲区也会在这里。
 *
 * 在支持128位整数的平台上，一些聚合使用基于128位整数的
 * 过渡数据类型来加快计算速度。
 *
 * ----------------------------------------------------------------------
 */

typedef struct NumericAggState
{
	bool		calcSumX2;		/* 如果为真，计算sumX2 */
	MemoryContext agg_context;	/* 我们正在计算的上下文 */
	int64		N;				/* 处理过的数字计数 */
	NumericSumAccum sumX;		/* 处理过的数字总和 */
	NumericSumAccum sumX2;		/* 处理过的数字的平方和 */
	int			maxScale;		/* 到目前为止看到的最大比例 */
	int64		maxScaleCount;	/* 以最大比例看到的值的数量 */
	/* 这些计数*不*包含在N中！根据需要使用NA_TOTAL_COUNT() */
	int64		NaNcount;		/* NaN值计数 */
	int64		pInfcount;		/* +Inf值计数 */
	int64		nInfcount;		/* -Inf值计数 */
} NumericAggState;

#define NA_TOTAL_COUNT(na) \
	((na)->N + (na)->NaNcount + (na)->pInfcount + (na)->nInfcount)

/*
 * 为需要计算输入的和、计数和可选的平方和的数字聚合函数准备状态数据。
 */
static NumericAggState *
fc_makeNumericAggState(FunctionCallInfo fcinfo, bool fc_calcSumX2)
{
	NumericAggState *fc_state;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_old_context = MemoryContextSwitchTo(fc_agg_context);

	fc_state = (NumericAggState *) palloc0(sizeof(NumericAggState));
	fc_state->calcSumX2 = fc_calcSumX2;
	fc_state->agg_context = fc_agg_context;

	MemoryContextSwitchTo(fc_old_context);

	return fc_state;
}

/*
 * 像makeNumericAggState()一样，但在当前内存上下文中分配状态。
 */
static NumericAggState *
fc_makeNumericAggStateCurrentContext(bool fc_calcSumX2)
{
	NumericAggState *fc_state;

	fc_state = (NumericAggState *) palloc0(sizeof(NumericAggState));
	fc_state->calcSumX2 = fc_calcSumX2;
	fc_state->agg_context = CurrentMemoryContext;

	return fc_state;
}

/*
 * 为数字聚合函数累积新输入值。
 */
static void fc_do_numeric_accum(NumericAggState *fc_state, Numeric fc_newval)
{
	NumericVar	fc_X;
	NumericVar	fc_X2;
	MemoryContext fc_old_context;

	/* 将NaN/无穷输入单独计数，与其他输入分开 */
	if (NUMERIC_IS_SPECIAL(fc_newval))
	{
		if (NUMERIC_IS_PINF(fc_newval))
			fc_state->pInfcount++;
		else if (NUMERIC_IS_NINF(fc_newval))
			fc_state->nInfcount++;
		else
			fc_state->NaNcount++;
		return;
	}

	/* 在短期上下文中加载处理过的数字 */
	fc_init_var_from_num(fc_newval, &fc_X);

	/*
	 * 跟踪我们所见过的最高输入 dscale，以支持反向转换
	 * （参见 do_numeric_discard）。
	 */
	if (fc_X.dscale > fc_state->maxScale)
	{
		fc_state->maxScale = fc_X.dscale;
		fc_state->maxScaleCount = 1;
	}
	else if (fc_X.dscale == fc_state->maxScale)
		fc_state->maxScaleCount++;

	/* 如果我们需要 X^2，在短期上下文中计算它 */
	if (fc_state->calcSumX2)
	{
		init_var(&fc_X2);
		fc_mul_var(&fc_X, &fc_X, &fc_X2, fc_X.dscale * 2);
	}

	/* 其余部分需要在聚合上下文中工作 */
	fc_old_context = MemoryContextSwitchTo(fc_state->agg_context);

	fc_state->N++;

	/* 累积求和 */
	fc_accum_sum_add(&(fc_state->sumX), &fc_X);

	if (fc_state->calcSumX2)
		fc_accum_sum_add(&(fc_state->sumX2), &fc_X2);

	MemoryContextSwitchTo(fc_old_context);
}

/*
 * 尝试从聚合状态中移除一个输入值。
 *
 * 如果无法移除该值，则函数将返回 false；失败的可能原因如下所述。
 *
 * 如果我们聚合值 1.01 和 2，则结果将是 3.01。
 * 如果我们被要求取消聚合 1.01，则必须失败，因为我们
 * 不知道新的聚合值的 dscale 应该是什么。
 * 我们不想返回 2.00（dscale = 2），因为如果我们真的
 * 只聚合了 2，则总和的 dscale 应该为零。
 *
 * 注意：另外，我们可以计算每个可能的 dscale 的输入数量
 * （最多到某个合理的限制）。目前尚不清楚是否值得这么做。
 */
static bool fc_do_numeric_discard(NumericAggState *fc_state, Numeric fc_newval)
{
	NumericVar	fc_X;
	NumericVar	fc_X2;
	MemoryContext fc_old_context;

	/* 将NaN/无穷输入单独计数，与其他输入分开 */
	if (NUMERIC_IS_SPECIAL(fc_newval))
	{
		if (NUMERIC_IS_PINF(fc_newval))
			fc_state->pInfcount--;
		else if (NUMERIC_IS_NINF(fc_newval))
			fc_state->nInfcount--;
		else
			fc_state->NaNcount--;
		return true;
	}

	/* 在短期上下文中加载处理过的数字 */
	fc_init_var_from_num(fc_newval, &fc_X);

	/*
	 * state->sumX 的 dscale 是所有输入中最大的 dscale。
	 * 移除最后一个具有该 dscale 的输入将要求我们重新计算
	 * 剩余输入的最大 dscale，除非没有更多非 NaN 的输入。
	 * 因此我们报告失败，而是强制重新从头开始聚合。
	 */
	if (fc_X.dscale == fc_state->maxScale)
	{
		if (fc_state->maxScaleCount > 1 || fc_state->maxScale == 0)
		{
			/*
			 * 一些剩余输入具有相同的 dscale，或者 dscale 无论如何都未超过零
			 */
			fc_state->maxScaleCount--;
		}
		else if (fc_state->N == 1)
		{
			/* 完全没有剩余的非 NaN 输入，因此重置 maxScale */
			fc_state->maxScale = 0;
			fc_state->maxScaleCount = 0;
		}
		else
		{
			/* 正确的新 maxScale 不确定，必须失败 */
			return false;
		}
	}

	/* 如果我们需要 X^2，在短期上下文中计算它 */
	if (fc_state->calcSumX2)
	{
		init_var(&fc_X2);
		fc_mul_var(&fc_X, &fc_X, &fc_X2, fc_X.dscale * 2);
	}

	/* 其余部分需要在聚合上下文中工作 */
	fc_old_context = MemoryContextSwitchTo(fc_state->agg_context);

	if (fc_state->N-- > 1)
	{
		/* 取反 X，以便从总和中减去它 */
		fc_X.sign = (fc_X.sign == NUMERIC_POS ? NUMERIC_NEG : NUMERIC_POS);
		fc_accum_sum_add(&(fc_state->sumX), &fc_X);

		if (fc_state->calcSumX2)
		{
			/* 取反 X^2。X^2 始终为正 */
			fc_X2.sign = NUMERIC_NEG;
			fc_accum_sum_add(&(fc_state->sumX2), &fc_X2);
		}
	}
	else
	{
		/* 清零总和 */
		Assert(fc_state->N == 0);

		fc_accum_sum_reset(&fc_state->sumX);
		if (fc_state->calcSumX2)
			fc_accum_sum_reset(&fc_state->sumX2);
	}

	MemoryContextSwitchTo(fc_old_context);

	return true;
}

/*
 * 需要 sumX2 的数字聚合的通用转换函数。
 */
Datum numeric_accum(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = fc_makeNumericAggState(fcinfo, true);

	if (!PG_ARGISNULL(1))
		fc_do_numeric_accum(fc_state, PG_GETARG_NUMERIC(1));

	PG_RETURN_POINTER(fc_state);
}

/*
 * 需要 sumX2 的数字聚合的通用合并函数
 */
Datum numeric_combine(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state1;
	NumericAggState *fc_state2;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
	fc_state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1);

	if (fc_state2 == NULL)
		PG_RETURN_POINTER(fc_state1);

	/* 手动将所有字段从 state2 复制到 state1 */
	if (fc_state1 == NULL)
	{
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		fc_state1 = fc_makeNumericAggStateCurrentContext(true);
		fc_state1->N = fc_state2->N;
		fc_state1->NaNcount = fc_state2->NaNcount;
		fc_state1->pInfcount = fc_state2->pInfcount;
		fc_state1->nInfcount = fc_state2->nInfcount;
		fc_state1->maxScale = fc_state2->maxScale;
		fc_state1->maxScaleCount = fc_state2->maxScaleCount;

		fc_accum_sum_copy(&fc_state1->sumX, &fc_state2->sumX);
		fc_accum_sum_copy(&fc_state1->sumX2, &fc_state2->sumX2);

		MemoryContextSwitchTo(fc_old_context);

		PG_RETURN_POINTER(fc_state1);
	}

	fc_state1->N += fc_state2->N;
	fc_state1->NaNcount += fc_state2->NaNcount;
	fc_state1->pInfcount += fc_state2->pInfcount;
	fc_state1->nInfcount += fc_state2->nInfcount;

	if (fc_state2->N > 0)
	{
		/*
		 * 这些当前仅在移动聚合时需要，但我们还是要
		 * 做正确的事情...
		 */
		if (fc_state2->maxScale > fc_state1->maxScale)
		{
			fc_state1->maxScale = fc_state2->maxScale;
			fc_state1->maxScaleCount = fc_state2->maxScaleCount;
		}
		else if (fc_state2->maxScale == fc_state1->maxScale)
			fc_state1->maxScaleCount += fc_state2->maxScaleCount;

		/* 其余部分需要在聚合上下文中工作 */
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		/* 累积求和 */
		fc_accum_sum_combine(&fc_state1->sumX, &fc_state2->sumX);
		fc_accum_sum_combine(&fc_state1->sumX2, &fc_state2->sumX2);

		MemoryContextSwitchTo(fc_old_context);
	}
	PG_RETURN_POINTER(fc_state1);
}

/*
 * 不需要 sumX2 的数字聚合的通用转换函数。
 */
Datum numeric_avg_accum(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = fc_makeNumericAggState(fcinfo, false);

	if (!PG_ARGISNULL(1))
		fc_do_numeric_accum(fc_state, PG_GETARG_NUMERIC(1));

	PG_RETURN_POINTER(fc_state);
}

/*
 * 不需要 sumX2 的数字聚合的合并函数
 */
Datum numeric_avg_combine(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state1;
	NumericAggState *fc_state2;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
	fc_state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1);

	if (fc_state2 == NULL)
		PG_RETURN_POINTER(fc_state1);

	/* 手动将所有字段从 state2 复制到 state1 */
	if (fc_state1 == NULL)
	{
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		fc_state1 = fc_makeNumericAggStateCurrentContext(false);
		fc_state1->N = fc_state2->N;
		fc_state1->NaNcount = fc_state2->NaNcount;
		fc_state1->pInfcount = fc_state2->pInfcount;
		fc_state1->nInfcount = fc_state2->nInfcount;
		fc_state1->maxScale = fc_state2->maxScale;
		fc_state1->maxScaleCount = fc_state2->maxScaleCount;

		fc_accum_sum_copy(&fc_state1->sumX, &fc_state2->sumX);

		MemoryContextSwitchTo(fc_old_context);

		PG_RETURN_POINTER(fc_state1);
	}

	fc_state1->N += fc_state2->N;
	fc_state1->NaNcount += fc_state2->NaNcount;
	fc_state1->pInfcount += fc_state2->pInfcount;
	fc_state1->nInfcount += fc_state2->nInfcount;

	if (fc_state2->N > 0)
	{
		/*
		 * 这些当前仅在移动聚合时需要，但我们还是要
		 * 做正确的事情...
		 */
		if (fc_state2->maxScale > fc_state1->maxScale)
		{
			fc_state1->maxScale = fc_state2->maxScale;
			fc_state1->maxScaleCount = fc_state2->maxScaleCount;
		}
		else if (fc_state2->maxScale == fc_state1->maxScale)
			fc_state1->maxScaleCount += fc_state2->maxScaleCount;

		/* 其余部分需要在聚合上下文中工作 */
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		/* 累积求和 */
		fc_accum_sum_combine(&fc_state1->sumX, &fc_state2->sumX);

		MemoryContextSwitchTo(fc_old_context);
	}
	PG_RETURN_POINTER(fc_state1);
}

/*
 * numeric_avg_serialize
 *		序列化 NumericAggState 用于不需要
 *		sumX2 的数字聚合。
 */
Datum numeric_avg_serialize(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	StringInfoData fc_buf;
	bytea	   *fc_result;
	NumericVar	fc_tmp_var;

	/* 确保在非聚合上下文中禁止调用 */
	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state = (NumericAggState *) PG_GETARG_POINTER(0);

	init_var(&fc_tmp_var);

	pq_begintypsend(&fc_buf);

	/* N */
	pq_sendint64(&fc_buf, fc_state->N);

	/* sumX */
	fc_accum_sum_final(&fc_state->sumX, &fc_tmp_var);
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	/* maxScale */
	pq_sendint32(&fc_buf, fc_state->maxScale);

	/* maxScaleCount */
	pq_sendint64(&fc_buf, fc_state->maxScaleCount);

	/* NaNcount */
	pq_sendint64(&fc_buf, fc_state->NaNcount);

	/* pInfcount */
	pq_sendint64(&fc_buf, fc_state->pInfcount);

	/* nInfcount */
	pq_sendint64(&fc_buf, fc_state->nInfcount);

	fc_result = pq_endtypsend(&fc_buf);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_BYTEA_P(fc_result);
}

/*
 * numeric_avg_deserialize
 *		反序列化 bytea 到 NumericAggState 用于
 *		不需要 sumX2 的数字聚合。
 */
Datum numeric_avg_deserialize(PG_FUNCTION_ARGS)
{
	bytea	   *fc_sstate;
	NumericAggState *fc_result;
	StringInfoData fc_buf;
	NumericVar	fc_tmp_var;

	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_sstate = PG_GETARG_BYTEA_PP(0);

	init_var(&fc_tmp_var);

	/*
	 * 将 bytea 复制到 StringInfo，以便我们可以使用
	 * 标准接收函数基础设施“接收”它。
	 */
	initStringInfo(&fc_buf);
	appendBinaryStringInfo(&fc_buf,
						   VARDATA_ANY(fc_sstate), VARSIZE_ANY_EXHDR(fc_sstate));

	fc_result = fc_makeNumericAggStateCurrentContext(false);

	/* N */
	fc_result->N = pq_getmsgint64(&fc_buf);

	/* sumX */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
	fc_accum_sum_add(&(fc_result->sumX), &fc_tmp_var);

	/* maxScale */
	fc_result->maxScale = pq_getmsgint(&fc_buf, 4);

	/* maxScaleCount */
	fc_result->maxScaleCount = pq_getmsgint64(&fc_buf);

	/* NaNcount */
	fc_result->NaNcount = pq_getmsgint64(&fc_buf);

	/* pInfcount */
	fc_result->pInfcount = pq_getmsgint64(&fc_buf);

	/* nInfcount */
	fc_result->nInfcount = pq_getmsgint64(&fc_buf);

	pq_getmsgend(&fc_buf);
	pfree(fc_buf.data);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_POINTER(fc_result);
}

/*
 * numeric_serialize
 *		用于需要 sumX2 的 NumericAggState 的序列化函数。
 */
Datum numeric_serialize(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	StringInfoData fc_buf;
	bytea	   *fc_result;
	NumericVar	fc_tmp_var;

	/* 确保在非聚合上下文中禁止调用 */
	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state = (NumericAggState *) PG_GETARG_POINTER(0);

	init_var(&fc_tmp_var);

	pq_begintypsend(&fc_buf);

	/* N */
	pq_sendint64(&fc_buf, fc_state->N);

	/* sumX */
	fc_accum_sum_final(&fc_state->sumX, &fc_tmp_var);
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	/* sumX2 */
	fc_accum_sum_final(&fc_state->sumX2, &fc_tmp_var);
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	/* maxScale */
	pq_sendint32(&fc_buf, fc_state->maxScale);

	/* maxScaleCount */
	pq_sendint64(&fc_buf, fc_state->maxScaleCount);

	/* NaNcount */
	pq_sendint64(&fc_buf, fc_state->NaNcount);

	/* pInfcount */
	pq_sendint64(&fc_buf, fc_state->pInfcount);

	/* nInfcount */
	pq_sendint64(&fc_buf, fc_state->nInfcount);

	fc_result = pq_endtypsend(&fc_buf);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_BYTEA_P(fc_result);
}

/*
 * numeric_deserialize
 *		用于需要 sumX2 的 NumericAggState 的反序列化函数。
 */
Datum numeric_deserialize(PG_FUNCTION_ARGS)
{
	bytea	   *fc_sstate;
	NumericAggState *fc_result;
	StringInfoData fc_buf;
	NumericVar	fc_tmp_var;

	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_sstate = PG_GETARG_BYTEA_PP(0);

	init_var(&fc_tmp_var);

	/*
	 * 将 bytea 复制到 StringInfo，以便我们可以使用
	 * 标准接收函数基础设施“接收”它。
	 */
	initStringInfo(&fc_buf);
	appendBinaryStringInfo(&fc_buf,
						   VARDATA_ANY(fc_sstate), VARSIZE_ANY_EXHDR(fc_sstate));

	fc_result = fc_makeNumericAggStateCurrentContext(false);

	/* N */
	fc_result->N = pq_getmsgint64(&fc_buf);

	/* sumX */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
	fc_accum_sum_add(&(fc_result->sumX), &fc_tmp_var);

	/* sumX2 */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
	fc_accum_sum_add(&(fc_result->sumX2), &fc_tmp_var);

	/* maxScale */
	fc_result->maxScale = pq_getmsgint(&fc_buf, 4);

	/* maxScaleCount */
	fc_result->maxScaleCount = pq_getmsgint64(&fc_buf);

	/* NaNcount */
	fc_result->NaNcount = pq_getmsgint64(&fc_buf);

	/* pInfcount */
	fc_result->pInfcount = pq_getmsgint64(&fc_buf);

	/* nInfcount */
	fc_result->nInfcount = pq_getmsgint64(&fc_buf);

	pq_getmsgend(&fc_buf);
	pfree(fc_buf.data);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_POINTER(fc_result);
}

/*
 * 数字聚合的通用反向转换函数
 * （无论是否需要 X^2）。
 */
Datum numeric_accum_inv(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 不应在没有状态的情况下到达这里 */
	if (fc_state == NULL)
		elog(ERROR, "numeric_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
		/* 如果我们未能执行反向转换，则返回 NULL */
		if (!fc_do_numeric_discard(fc_state, PG_GETARG_NUMERIC(1)))
			PG_RETURN_NULL();
	}

	PG_RETURN_POINTER(fc_state);
}


/*
 * 整数数据类型通常使用数字累加器来共享代码
 * 并避免溢出的风险。
 *
 * 但是出于性能原因，尽可能使用优化的专用累加器
 * 例程。
 *
 * 在支持 128 位整数的平台上，当 sum(X) 或 sum(X*X) 能够
 * 放入 128 位时，将使用 128 位例程。
 *
 * 对于 16 位和 32 位输入，N 和 sum(X) 能够放入 64 位，因此
 * 将使用 64 位累加器用于这些数据类型的 SUM 和 AVG。
 */

#ifdef HAVE_INT128
typedef struct Int128AggState
{
	bool		calcSumX2;		/* 如果为真，计算sumX2 */
	int64		N;				/* 处理过的数字计数 */
	int128		sumX;			/* 处理过的数字总和 */
	int128		sumX2;			/* 处理过的数字的平方和 */
} Int128AggState;

/*
 * 为需要计算
 * sum、count 和可选的输入平方和的 128 位聚合函数准备状态数据。
 */
static Int128AggState *
fc_makeInt128AggState(FunctionCallInfo fcinfo, bool fc_calcSumX2)
{
	Int128AggState *fc_state;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_old_context = MemoryContextSwitchTo(fc_agg_context);

	fc_state = (Int128AggState *) palloc0(sizeof(Int128AggState));
	fc_state->calcSumX2 = fc_calcSumX2;

	MemoryContextSwitchTo(fc_old_context);

	return fc_state;
}

/*
 * 像 makeInt128AggState()，但在当前内存
 * 上下文中分配状态。
 */
static Int128AggState *
fc_makeInt128AggStateCurrentContext(bool fc_calcSumX2)
{
	Int128AggState *fc_state;

	fc_state = (Int128AggState *) palloc0(sizeof(Int128AggState));
	fc_state->calcSumX2 = fc_calcSumX2;

	return fc_state;
}

/*
 * 为 128 位聚合函数累积一个新的输入值。
 */
static void fc_do_int128_accum(Int128AggState *fc_state, int128 fc_newval)
{
	if (fc_state->calcSumX2)
		fc_state->sumX2 += fc_newval * fc_newval;

	fc_state->sumX += fc_newval;
	fc_state->N++;
}

/*
 * 从聚合状态中移除一个输入值。
 */
static void fc_do_int128_discard(Int128AggState *fc_state, int128 fc_newval)
{
	if (fc_state->calcSumX2)
		fc_state->sumX2 -= fc_newval * fc_newval;

	fc_state->sumX -= fc_newval;
	fc_state->N--;
}

typedef Int128AggState PolyNumAggState;
#define makePolyNumAggState fc_makeInt128AggState
#define makePolyNumAggStateCurrentContext fc_makeInt128AggStateCurrentContext
#else
typedef NumericAggState PolyNumAggState;
#define makePolyNumAggState makeNumericAggState
#define makePolyNumAggStateCurrentContext makeNumericAggStateCurrentContext
#endif

Datum int2_accum(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = makePolyNumAggState(fcinfo, true);

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_accum(fc_state, (int128) PG_GETARG_INT16(1));
#else
		fc_do_numeric_accum(fc_state, int64_to_numeric(PG_GETARG_INT16(1)));
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

Datum int4_accum(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = makePolyNumAggState(fcinfo, true);

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_accum(fc_state, (int128) PG_GETARG_INT32(1));
#else
		fc_do_numeric_accum(fc_state, int64_to_numeric(PG_GETARG_INT32(1)));
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

Datum int8_accum(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = fc_makeNumericAggState(fcinfo, true);

	if (!PG_ARGISNULL(1))
		fc_do_numeric_accum(fc_state, int64_to_numeric(PG_GETARG_INT64(1)));

	PG_RETURN_POINTER(fc_state);
}

/*
 * 需要 sumX2 的数字聚合的合并功能
 */
Datum numeric_poly_combine(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state1;
	PolyNumAggState *fc_state2;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state1 = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);
	fc_state2 = PG_ARGISNULL(1) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(1);

	if (fc_state2 == NULL)
		PG_RETURN_POINTER(fc_state1);

	/* 手动将所有字段从 state2 复制到 state1 */
	if (fc_state1 == NULL)
	{
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		fc_state1 = makePolyNumAggState(fcinfo, true);
		fc_state1->N = fc_state2->N;

#ifdef HAVE_INT128
		fc_state1->sumX = fc_state2->sumX;
		fc_state1->sumX2 = fc_state2->sumX2;
#else
		accum_sum_copy(&fc_state1->sumX, &fc_state2->sumX);
		accum_sum_copy(&fc_state1->sumX2, &fc_state2->sumX2);
#endif

		MemoryContextSwitchTo(fc_old_context);

		PG_RETURN_POINTER(fc_state1);
	}

	if (fc_state2->N > 0)
	{
		fc_state1->N += fc_state2->N;

#ifdef HAVE_INT128
		fc_state1->sumX += fc_state2->sumX;
		fc_state1->sumX2 += fc_state2->sumX2;
#else
		/* 其余部分需要在聚合上下文中工作 */
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		/* 累积求和 */
		fc_accum_sum_combine(&fc_state1->sumX, &fc_state2->sumX);
		fc_accum_sum_combine(&fc_state1->sumX2, &fc_state2->sumX2);

		MemoryContextSwitchTo(fc_old_context);
#endif

	}
	PG_RETURN_POINTER(fc_state1);
}

/*
 * numeric_poly_serialize
 *		序列化 PolyNumAggState 为 bytea，适用于
 *		需要 sumX2 的聚合函数。
 */
Datum numeric_poly_serialize(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;
	StringInfoData fc_buf;
	bytea	   *fc_result;
	NumericVar	fc_tmp_var;

	/* 确保在非聚合上下文中禁止调用 */
	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state = (PolyNumAggState *) PG_GETARG_POINTER(0);

	/*
	 * 如果平台支持 int128，则 sumX 和 sumX2 将是 128 位
	 * 整数类型。这里我们会将其转换为数字类型，以便合并状态
	 * 在支持 int128 的机器和不支持该类型的机器之间保持相同
	 * 的格式。这里的逻辑是，有一天我们可能想将它们发送到
	 * 另一台服务器进行进一步处理，我们希望有一个标准格式
	 * 来进行操作。
	 */

	init_var(&fc_tmp_var);

	pq_begintypsend(&fc_buf);

	/* N */
	pq_sendint64(&fc_buf, fc_state->N);

	/* sumX */
#ifdef HAVE_INT128
	fc_int128_to_numericvar(fc_state->sumX, &fc_tmp_var);
#else
	fc_accum_sum_final(&fc_state->sumX, &fc_tmp_var);
#endif
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	/* sumX2 */
#ifdef HAVE_INT128
	fc_int128_to_numericvar(fc_state->sumX2, &fc_tmp_var);
#else
	fc_accum_sum_final(&fc_state->sumX2, &fc_tmp_var);
#endif
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	fc_result = pq_endtypsend(&fc_buf);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_BYTEA_P(fc_result);
}

/*
 * numeric_poly_deserialize
 *		从 bytea 反序列化 PolyNumAggState，适用于
 *		需要 sumX2 的聚合函数。
 */
Datum numeric_poly_deserialize(PG_FUNCTION_ARGS)
{
	bytea	   *fc_sstate;
	PolyNumAggState *fc_result;
	StringInfoData fc_buf;
	NumericVar	fc_tmp_var;

	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_sstate = PG_GETARG_BYTEA_PP(0);

	init_var(&fc_tmp_var);

	/*
	 * 将 bytea 复制到 StringInfo，以便我们可以使用
	 * 标准接收函数基础设施“接收”它。
	 */
	initStringInfo(&fc_buf);
	appendBinaryStringInfo(&fc_buf,
						   VARDATA_ANY(fc_sstate), VARSIZE_ANY_EXHDR(fc_sstate));

	fc_result = makePolyNumAggStateCurrentContext(false);

	/* N */
	fc_result->N = pq_getmsgint64(&fc_buf);

	/* sumX */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
#ifdef HAVE_INT128
	fc_numericvar_to_int128(&fc_tmp_var, &fc_result->sumX);
#else
	fc_accum_sum_add(&fc_result->sumX, &fc_tmp_var);
#endif

	/* sumX2 */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
#ifdef HAVE_INT128
	fc_numericvar_to_int128(&fc_tmp_var, &fc_result->sumX2);
#else
	fc_accum_sum_add(&fc_result->sumX2, &fc_tmp_var);
#endif

	pq_getmsgend(&fc_buf);
	pfree(fc_buf.data);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_POINTER(fc_result);
}

/*
 * 当我们不需要 sumX2 时，针对 int8 输入的转换函数。
 */
Datum int8_avg_accum(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 在第一次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = makePolyNumAggState(fcinfo, false);

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_accum(fc_state, (int128) PG_GETARG_INT64(1));
#else
		fc_do_numeric_accum(fc_state, int64_to_numeric(PG_GETARG_INT64(1)));
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

/*
 * 针对不需要 sumX2 的聚合的 PolyNumAggState 的合并函数
 */
Datum int8_avg_combine(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state1;
	PolyNumAggState *fc_state2;
	MemoryContext fc_agg_context;
	MemoryContext fc_old_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state1 = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);
	fc_state2 = PG_ARGISNULL(1) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(1);

	if (fc_state2 == NULL)
		PG_RETURN_POINTER(fc_state1);

	/* 手动将所有字段从 state2 复制到 state1 */
	if (fc_state1 == NULL)
	{
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		fc_state1 = makePolyNumAggState(fcinfo, false);
		fc_state1->N = fc_state2->N;

#ifdef HAVE_INT128
		fc_state1->sumX = fc_state2->sumX;
#else
		fc_accum_sum_copy(&fc_state1->sumX, &fc_state2->sumX);
#endif
		MemoryContextSwitchTo(fc_old_context);

		PG_RETURN_POINTER(fc_state1);
	}

	if (fc_state2->N > 0)
	{
		fc_state1->N += fc_state2->N;

#ifdef HAVE_INT128
		fc_state1->sumX += fc_state2->sumX;
#else
		/* 其余部分需要在聚合上下文中工作 */
		fc_old_context = MemoryContextSwitchTo(fc_agg_context);

		/* 累积求和 */
		fc_accum_sum_combine(&fc_state1->sumX, &fc_state2->sumX);

		MemoryContextSwitchTo(fc_old_context);
#endif

	}
	PG_RETURN_POINTER(fc_state1);
}

/*
 * int8_avg_serialize
 *		使用标准
 *		接收函数基础设施将 PolyNumAggState 序列化为 bytea。
 */
Datum int8_avg_serialize(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;
	StringInfoData fc_buf;
	bytea	   *fc_result;
	NumericVar	fc_tmp_var;

	/* 确保在非聚合上下文中禁止调用 */
	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state = (PolyNumAggState *) PG_GETARG_POINTER(0);

	/*
	 * 如果平台支持 int128，则 sumX 将是 128 整数类型。
	 * 在这里我们将其转换为数字类型，以便合并状态
	 * 在支持 int128 的机器和不支持该类型的机器之间保持相同
	 * 的格式。这里的逻辑是，有一天我们可能想将它们发送到
	 * 另一台服务器进行进一步处理，我们希望有一个标准格式
	 * 来进行操作。
	 */

	init_var(&fc_tmp_var);

	pq_begintypsend(&fc_buf);

	/* N */
	pq_sendint64(&fc_buf, fc_state->N);

	/* sumX */
#ifdef HAVE_INT128
	fc_int128_to_numericvar(fc_state->sumX, &fc_tmp_var);
#else
	fc_accum_sum_final(&fc_state->sumX, &fc_tmp_var);
#endif
	fc_numericvar_serialize(&fc_buf, &fc_tmp_var);

	fc_result = pq_endtypsend(&fc_buf);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_BYTEA_P(fc_result);
}

/*
 * int8_avg_deserialize
 *		将 bytea 反序列化回 PolyNumAggState。
 */
Datum int8_avg_deserialize(PG_FUNCTION_ARGS)
{
	bytea	   *fc_sstate;
	PolyNumAggState *fc_result;
	StringInfoData fc_buf;
	NumericVar	fc_tmp_var;

	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_sstate = PG_GETARG_BYTEA_PP(0);

	init_var(&fc_tmp_var);

	/*
	 * 将 bytea 复制到 StringInfo，以便我们可以使用
	 * 标准接收函数基础设施“接收”它。
	 */
	initStringInfo(&fc_buf);
	appendBinaryStringInfo(&fc_buf,
						   VARDATA_ANY(fc_sstate), VARSIZE_ANY_EXHDR(fc_sstate));

	fc_result = makePolyNumAggStateCurrentContext(false);

	/* N */
	fc_result->N = pq_getmsgint64(&fc_buf);

	/* sumX */
	fc_numericvar_deserialize(&fc_buf, &fc_tmp_var);
#ifdef HAVE_INT128
	fc_numericvar_to_int128(&fc_tmp_var, &fc_result->sumX);
#else
	fc_accum_sum_add(&fc_result->sumX, &fc_tmp_var);
#endif

	pq_getmsgend(&fc_buf);
	pfree(fc_buf.data);

	fc_free_var(&fc_tmp_var);

	PG_RETURN_POINTER(fc_result);
}

/*
 * 与上述内容配对的反向转换函数。
 */

Datum int2_accum_inv(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 不应在没有状态的情况下到达这里 */
	if (fc_state == NULL)
		elog(ERROR, "int2_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_discard(fc_state, (int128) PG_GETARG_INT16(1));
#else
		/* 不应失败，所有输入的 dscale 为 0 */
		if (!do_numeric_discard(fc_state, int64_to_numeric(PG_GETARG_INT16(1))))
			elog(ERROR, "do_numeric_discard failed unexpectedly");
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

Datum int4_accum_inv(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 不应在没有状态的情况下到达这里 */
	if (fc_state == NULL)
		elog(ERROR, "int4_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_discard(fc_state, (int128) PG_GETARG_INT32(1));
#else
		/* 不应失败，所有输入的 dscale 为 0 */
		if (!do_numeric_discard(fc_state, int64_to_numeric(PG_GETARG_INT32(1))))
			elog(ERROR, "do_numeric_discard failed unexpectedly");
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

Datum int8_accum_inv(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 不应在没有状态的情况下到达这里 */
	if (fc_state == NULL)
		elog(ERROR, "int8_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
		/* 不应失败，所有输入的 dscale 为 0 */
		if (!fc_do_numeric_discard(fc_state, int64_to_numeric(PG_GETARG_INT64(1))))
			elog(ERROR, "do_numeric_discard failed unexpectedly");
	}

	PG_RETURN_POINTER(fc_state);
}

Datum int8_avg_accum_inv(PG_FUNCTION_ARGS)
{
	PolyNumAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 不应在没有状态的情况下到达这里 */
	if (fc_state == NULL)
		elog(ERROR, "int8_avg_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
#ifdef HAVE_INT128
		fc_do_int128_discard(fc_state, (int128) PG_GETARG_INT64(1));
#else
		/* 不应失败，所有输入的 dscale 为 0 */
		if (!fc_do_numeric_discard(fc_state, int64_to_numeric(PG_GETARG_INT64(1))))
			elog(ERROR, "do_numeric_discard failed unexpectedly");
#endif
	}

	PG_RETURN_POINTER(fc_state);
}

Datum numeric_poly_sum(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	Numeric		fc_res;
	NumericVar	fc_result;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空输入，则返回 NULL */
	if (fc_state == NULL || fc_state->N == 0)
		PG_RETURN_NULL();

	init_var(&fc_result);

	fc_int128_to_numericvar(fc_state->sumX, &fc_result);

	fc_res = fc_make_result(&fc_result);

	fc_free_var(&fc_result);

	PG_RETURN_NUMERIC(fc_res);
#else
	return numeric_sum(fcinfo);
#endif
}

Datum numeric_poly_avg(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	NumericVar	fc_result;
	Datum		fc_countd,
				fc_sumd;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空输入，则返回 NULL */
	if (fc_state == NULL || fc_state->N == 0)
		PG_RETURN_NULL();

	init_var(&fc_result);

	fc_int128_to_numericvar(fc_state->sumX, &fc_result);

	fc_countd = NumericGetDatum(int64_to_numeric(fc_state->N));
	fc_sumd = NumericGetDatum(fc_make_result(&fc_result));

	fc_free_var(&fc_result);

	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, fc_sumd, fc_countd));
#else
	return numeric_avg(fcinfo);
#endif
}

Datum numeric_avg(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	Datum		fc_N_datum;
	Datum		fc_sumX_datum;
	NumericVar	fc_sumX_var;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空输入，则返回 NULL */
	if (fc_state == NULL || NA_TOTAL_COUNT(fc_state) == 0)
		PG_RETURN_NULL();

	if (fc_state->NaNcount > 0)	/* 至少有一个 NaN 输入 */
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	/* 加上正负无穷大得到 NaN */
	if (fc_state->pInfcount > 0 && fc_state->nInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));
	if (fc_state->pInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	if (fc_state->nInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_ninf));

	fc_N_datum = NumericGetDatum(int64_to_numeric(fc_state->N));

	init_var(&fc_sumX_var);
	fc_accum_sum_final(&fc_state->sumX, &fc_sumX_var);
	fc_sumX_datum = NumericGetDatum(fc_make_result(&fc_sumX_var));
	fc_free_var(&fc_sumX_var);

	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, fc_sumX_datum, fc_N_datum));
}

Datum numeric_sum(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	NumericVar	fc_sumX_var;
	Numeric		fc_result;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空输入，则返回 NULL */
	if (fc_state == NULL || NA_TOTAL_COUNT(fc_state) == 0)
		PG_RETURN_NULL();

	if (fc_state->NaNcount > 0)	/* 至少有一个 NaN 输入 */
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));

	/* 加上正负无穷大得到 NaN */
	if (fc_state->pInfcount > 0 && fc_state->nInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_nan));
	if (fc_state->pInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_pinf));
	if (fc_state->nInfcount > 0)
		PG_RETURN_NUMERIC(fc_make_result(&const_ninf));

	init_var(&fc_sumX_var);
	fc_accum_sum_final(&fc_state->sumX, &fc_sumX_var);
	fc_result = fc_make_result(&fc_sumX_var);
	fc_free_var(&fc_sumX_var);

	PG_RETURN_NUMERIC(fc_result);
}

/*
 * 标准偏差和方差聚合的工作例程。'state' 是聚合的
 * 转换状态。'variance' 指定我们是否应该计算
 * 方差或标准偏差。'sample' 表示调用者是否对样本或
 * 总体方差/标准偏差感兴趣。
 *
 * 如果输入的适当方差统计量未定义，
 * *is_null 被设置为 true，返回 NULL。
 */
static Numeric fc_numeric_stddev_internal(NumericAggState *fc_state,
						bool fc_variance, bool fc_sample,
						bool *fc_is_null)
{
	Numeric		fc_res;
	NumericVar	fc_vN,
				fc_vsumX,
				fc_vsumX2,
				fc_vNminus1;
	int64		fc_totCount;
	int			fc_rscale;

	/*
	 * 当 N <= 1 时，样本标准差和方差未定义；当 N == 0
	 * 时，总体标准差未定义。在这两种情况下返回 NULL（注意
	 * NaN 和无穷大在此目的下被视为正常输入）。
	 */
	if (fc_state == NULL || (fc_totCount = NA_TOTAL_COUNT(fc_state)) == 0)
	{
		*fc_is_null = true;
		return NULL;
	}

	if (fc_sample && fc_totCount <= 1)
	{
		*fc_is_null = true;
		return NULL;
	}

	*fc_is_null = false;

	/*
	 * 处理 NaN 和无穷大情况。根据 float8 函数的行为，
	 * 任何无穷大输入都会产生 NaN 输出。
	 */
	if (fc_state->NaNcount > 0 || fc_state->pInfcount > 0 || fc_state->nInfcount > 0)
		return fc_make_result(&const_nan);

	/* 好的，正常计算适用 */
	init_var(&fc_vN);
	init_var(&fc_vsumX);
	init_var(&fc_vsumX2);

	fc_int64_to_numericvar(fc_state->N, &fc_vN);
	fc_accum_sum_final(&(fc_state->sumX), &fc_vsumX);
	fc_accum_sum_final(&(fc_state->sumX2), &fc_vsumX2);

	init_var(&fc_vNminus1);
	fc_sub_var(&fc_vN, &const_one, &fc_vNminus1);

	/* 为 mul_var 调用计算 rscale */
	fc_rscale = fc_vsumX.dscale * 2;

	fc_mul_var(&fc_vsumX, &fc_vsumX, &fc_vsumX, fc_rscale);	/* vsumX = sumX * sumX */
	fc_mul_var(&fc_vN, &fc_vsumX2, &fc_vsumX2, fc_rscale); /* vsumX2 = N * sumX2 */
	fc_sub_var(&fc_vsumX2, &fc_vsumX, &fc_vsumX2);	/* N * sumX2 - sumX * sumX */

	if (fc_cmp_var(&fc_vsumX2, &const_zero) <= 0)
	{
		/* 注意舍入误差产生负的分子 */
		fc_res = fc_make_result(&const_zero);
	}
	else
	{
		if (fc_sample)
			fc_mul_var(&fc_vN, &fc_vNminus1, &fc_vNminus1, 0);	/* N * (N - 1) */
		else
			fc_mul_var(&fc_vN, &fc_vN, &fc_vNminus1, 0);	/* N * N */
		fc_rscale = fc_select_div_scale(&fc_vsumX2, &fc_vNminus1);
		fc_div_var(&fc_vsumX2, &fc_vNminus1, &fc_vsumX, fc_rscale, true);	/* variance */
		if (!fc_variance)
			fc_sqrt_var(&fc_vsumX, &fc_vsumX, fc_rscale);	/* stddev */

		fc_res = fc_make_result(&fc_vsumX);
	}

	fc_free_var(&fc_vNminus1);
	fc_free_var(&fc_vsumX);
	fc_free_var(&fc_vsumX2);

	return fc_res;
}

Datum numeric_var_samp(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_stddev_internal(fc_state, true, true, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
}

Datum numeric_stddev_samp(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_stddev_internal(fc_state, false, true, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
}

Datum numeric_var_pop(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_stddev_internal(fc_state, true, false, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
}

Datum numeric_stddev_pop(PG_FUNCTION_ARGS)
{
	NumericAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_stddev_internal(fc_state, false, false, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
}

#ifdef HAVE_INT128
static Numeric fc_numeric_poly_stddev_internal(Int128AggState *fc_state,
							 bool fc_variance, bool fc_sample,
							 bool *fc_is_null)
{
	NumericAggState fc_numstate;
	Numeric		fc_res;

	/* 初始化一个空的聚合状态 */
	memset(&fc_numstate, 0, sizeof(NumericAggState));

	if (fc_state)
	{
		NumericVar	fc_tmp_var;

		fc_numstate.N = fc_state->N;

		init_var(&fc_tmp_var);

		fc_int128_to_numericvar(fc_state->sumX, &fc_tmp_var);
		fc_accum_sum_add(&fc_numstate.sumX, &fc_tmp_var);

		fc_int128_to_numericvar(fc_state->sumX2, &fc_tmp_var);
		fc_accum_sum_add(&fc_numstate.sumX2, &fc_tmp_var);

		fc_free_var(&fc_tmp_var);
	}

	fc_res = fc_numeric_stddev_internal(&fc_numstate, fc_variance, fc_sample, fc_is_null);

	if (fc_numstate.sumX.ndigits > 0)
	{
		pfree(fc_numstate.sumX.pos_digits);
		pfree(fc_numstate.sumX.neg_digits);
	}
	if (fc_numstate.sumX2.ndigits > 0)
	{
		pfree(fc_numstate.sumX2.pos_digits);
		pfree(fc_numstate.sumX2.neg_digits);
	}

	return fc_res;
}
#endif

Datum numeric_poly_var_samp(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_poly_stddev_internal(fc_state, true, true, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
#else
	return numeric_var_samp(fcinfo);
#endif
}

Datum numeric_poly_stddev_samp(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_poly_stddev_internal(fc_state, false, true, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
#else
	return numeric_stddev_samp(fcinfo);
#endif
}

Datum numeric_poly_var_pop(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_poly_stddev_internal(fc_state, true, false, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
#else
	return numeric_var_pop(fcinfo);
#endif
}

Datum numeric_poly_stddev_pop(PG_FUNCTION_ARGS)
{
#ifdef HAVE_INT128
	PolyNumAggState *fc_state;
	Numeric		fc_res;
	bool		fc_is_null;

	fc_state = PG_ARGISNULL(0) ? NULL : (PolyNumAggState *) PG_GETARG_POINTER(0);

	fc_res = fc_numeric_poly_stddev_internal(fc_state, false, false, &fc_is_null);

	if (fc_is_null)
		PG_RETURN_NULL();
	else
		PG_RETURN_NUMERIC(fc_res);
#else
	return numeric_stddev_pop(fcinfo);
#endif
}

/*
 * 整数数据类型的SUM转换函数。
 *
 * 为了避免溢出，我们使用比输入数据类型更宽的累加器。
 * 对于int8输入，我们需要一个Numeric累加器；对于int4和int2
 * 输入，我们使用int8累加器，这在实际使用中应该足够。 （后两个因此实际上不应该在这个文件中，
 * 但我们还是把它们留在这里。）
 *
 * 因为SQL定义SUM()对于零个值返回NULL，而不是零，
 * 所以转换数据值的初始条件需要是NULL。这
 * 意味着我们不能依靠ExecAgg自动将第一个非空
 * 数据值插入转换数据：它不知道如何进行类型
 * 转换。 结果是，这些例程必须标记为非严格
 * ，并自己处理第一个非空输入的替代。
 *
 * 注意：这些函数仅在普通聚合模式下使用。
 * 在移动聚合模式下，我们使用intX_avg_accum和intX_avg_accum_inv。
 */

Datum int2_sum(PG_FUNCTION_ARGS)
{
	int64		fc_newval;

	if (PG_ARGISNULL(0))
	{
		/* 到目前为止未见非空输入... */
		if (PG_ARGISNULL(1))
			PG_RETURN_NULL();	/* 仍然没有非空 */
		/* 这是第一个非空输入。 */
		fc_newval = (int64) PG_GETARG_INT16(1);
		PG_RETURN_INT64(fc_newval);
	}

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来避免palloc开销。如果没有，我们需要返回
	 * 转换变量的新值。（如果int8是按值传递，
	 * 那么当然这也是无用且不正确的，所以只需#ifdef它
	 * 出去。）
	 */
#ifndef USE_FLOAT8_BYVAL		/* 也控制 int8 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		int64	   *fc_oldsum = (int64 *) PG_GETARG_POINTER(0);

		/* 新输入为null时保持运行总和不变 */
		if (!PG_ARGISNULL(1))
			*fc_oldsum = *fc_oldsum + (int64) PG_GETARG_INT16(1);

		PG_RETURN_POINTER(fc_oldsum);
	}
	else
#endif
	{
		int64		fc_oldsum = PG_GETARG_INT64(0);

		/* 如果新输入为null，保持总和不变。 */
		if (PG_ARGISNULL(1))
			PG_RETURN_INT64(fc_oldsum);

		/* 可以进行加法。 */
		fc_newval = fc_oldsum + (int64) PG_GETARG_INT16(1);

		PG_RETURN_INT64(fc_newval);
	}
}

Datum int4_sum(PG_FUNCTION_ARGS)
{
	int64		fc_newval;

	if (PG_ARGISNULL(0))
	{
		/* 到目前为止未见非空输入... */
		if (PG_ARGISNULL(1))
			PG_RETURN_NULL();	/* 仍然没有非空 */
		/* 这是第一个非空输入。 */
		fc_newval = (int64) PG_GETARG_INT32(1);
		PG_RETURN_INT64(fc_newval);
	}

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来避免palloc开销。如果没有，我们需要返回
	 * 转换变量的新值。（如果int8是按值传递，
	 * 那么当然这也是无用且不正确的，所以只需#ifdef它
	 * 出去。）
	 */
#ifndef USE_FLOAT8_BYVAL		/* 也控制 int8 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		int64	   *fc_oldsum = (int64 *) PG_GETARG_POINTER(0);

		/* 新输入为null时保持运行总和不变 */
		if (!PG_ARGISNULL(1))
			*fc_oldsum = *fc_oldsum + (int64) PG_GETARG_INT32(1);

		PG_RETURN_POINTER(fc_oldsum);
	}
	else
#endif
	{
		int64		fc_oldsum = PG_GETARG_INT64(0);

		/* 如果新输入为null，保持总和不变。 */
		if (PG_ARGISNULL(1))
			PG_RETURN_INT64(fc_oldsum);

		/* 可以进行加法。 */
		fc_newval = fc_oldsum + (int64) PG_GETARG_INT32(1);

		PG_RETURN_INT64(fc_newval);
	}
}

/*
 * 注意：这个函数已经过时，不再用于SUM(int8)。
 */
Datum int8_sum(PG_FUNCTION_ARGS)
{
	Numeric		fc_oldsum;

	if (PG_ARGISNULL(0))
	{
		/* 到目前为止未见非空输入... */
		if (PG_ARGISNULL(1))
			PG_RETURN_NULL();	/* 仍然没有非空 */
		/* 这是第一个非空输入。 */
		PG_RETURN_NUMERIC(int64_to_numeric(PG_GETARG_INT64(1)));
	}

	/*
	 * 请注意，我们不能像int2_sum和int4_sum那样在这里对聚合情况进行特例处理：
	 * numeric的大小是可变的，因此我们不能原地修改
	 * 第一个参数。
	 */

	fc_oldsum = PG_GETARG_NUMERIC(0);

	/* 如果新输入为null，保持总和不变。 */
	if (PG_ARGISNULL(1))
		PG_RETURN_NUMERIC(fc_oldsum);

	/* 可以进行加法。 */
	PG_RETURN_DATUM(DirectFunctionCall2(numeric_add,
										NumericGetDatum(fc_oldsum),
										NumericGetDatum(int64_to_numeric(PG_GETARG_INT64(1)))));
}


/*
 * avg(int2)和avg(int4)的例程。 转换数据类型
 * 是一个包含计数和总和的双元素int8数组。
 *
 * 这些函数在移动聚合模式下也用于sum(int2)和sum(int4)
 * ，因为为了正确的逆转变，我们需要计算输入。
 */

typedef struct Int8TransTypeData
{
	int64		count;
	int64		sum;
} Int8TransTypeData;

Datum int2_avg_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray;
	int16		fc_newval = PG_GETARG_INT16(1);
	Int8TransTypeData *fc_transdata;

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来减少palloc开销。否则我们需要在修改之前进行
	 * 复制。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
		fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	else
		fc_transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);
	fc_transdata->count++;
	fc_transdata->sum += fc_newval;

	PG_RETURN_ARRAYTYPE_P(fc_transarray);
}

Datum int4_avg_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray;
	int32		fc_newval = PG_GETARG_INT32(1);
	Int8TransTypeData *fc_transdata;

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来减少palloc开销。否则我们需要在修改之前进行
	 * 复制。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
		fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	else
		fc_transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);
	fc_transdata->count++;
	fc_transdata->sum += fc_newval;

	PG_RETURN_ARRAYTYPE_P(fc_transarray);
}

Datum int4_avg_combine(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray1;
	ArrayType  *fc_transarray2;
	Int8TransTypeData *fc_state1;
	Int8TransTypeData *fc_state2;

	if (!AggCheckCallContext(fcinfo, NULL))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_transarray1 = PG_GETARG_ARRAYTYPE_P(0);
	fc_transarray2 = PG_GETARG_ARRAYTYPE_P(1);

	if (ARR_HASNULL(fc_transarray1) ||
		ARR_SIZE(fc_transarray1) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	if (ARR_HASNULL(fc_transarray2) ||
		ARR_SIZE(fc_transarray2) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	fc_state1 = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray1);
	fc_state2 = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray2);

	fc_state1->count += fc_state2->count;
	fc_state1->sum += fc_state2->sum;

	PG_RETURN_ARRAYTYPE_P(fc_transarray1);
}

Datum int2_avg_accum_inv(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray;
	int16		fc_newval = PG_GETARG_INT16(1);
	Int8TransTypeData *fc_transdata;

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来减少palloc开销。否则我们需要在修改之前进行
	 * 复制。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
		fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	else
		fc_transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);
	fc_transdata->count--;
	fc_transdata->sum -= fc_newval;

	PG_RETURN_ARRAYTYPE_P(fc_transarray);
}

Datum int4_avg_accum_inv(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray;
	int32		fc_newval = PG_GETARG_INT32(1);
	Int8TransTypeData *fc_transdata;

	/*
	 * 如果我们作为聚合被调用，我们可以通过原地修改第一个
	 * 参数来减少palloc开销。否则我们需要在修改之前进行
	 * 复制。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
		fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	else
		fc_transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");

	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);
	fc_transdata->count--;
	fc_transdata->sum -= fc_newval;

	PG_RETURN_ARRAYTYPE_P(fc_transarray);
}

Datum int8_avg(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	Int8TransTypeData *fc_transdata;
	Datum		fc_countd,
				fc_sumd;

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");
	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);

	/* SQL 定义零值的 AVG 为 NULL */
	if (fc_transdata->count == 0)
		PG_RETURN_NULL();

	fc_countd = NumericGetDatum(int64_to_numeric(fc_transdata->count));
	fc_sumd = NumericGetDatum(int64_to_numeric(fc_transdata->sum));

	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, fc_sumd, fc_countd));
}

/*
 * SUM(int2)和SUM(int4)都返回int8，所以我们可以
 * 将这个最终函数用于两者。
 */
Datum int2int4_sum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	Int8TransTypeData *fc_transdata;

	if (ARR_HASNULL(fc_transarray) ||
		ARR_SIZE(fc_transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
		elog(ERROR, "expected 2-element int8 array");
	fc_transdata = (Int8TransTypeData *) ARR_DATA_PTR(fc_transarray);

	/* SQL定义零值的SUM为NULL */
	if (fc_transdata->count == 0)
		PG_RETURN_NULL();

	PG_RETURN_DATUM(Int64GetDatumFast(fc_transdata->sum));
}


/* ----------------------------------------------------------------------
 *
 * 调试支持
 *
 * ----------------------------------------------------------------------
 */

#ifdef NUMERIC_DEBUG

/*
 * dump_numeric() - 以数据库存储格式转储值以进行调试
 */
static void fc_dump_numeric(const char *fc_str, Numeric fc_num)
{
	NumericDigit *fc_digits = NUMERIC_DIGITS(fc_num);
	int			fc_ndigits;
	int			fc_i;

	fc_ndigits = NUMERIC_NDIGITS(fc_num);

	printf("%s: NUMERIC w=%d d=%d ", fc_str,
		   NUMERIC_WEIGHT(fc_num), NUMERIC_DSCALE(fc_num));
	switch (NUMERIC_SIGN(fc_num))
	{
		case NUMERIC_POS:
			printf("POS");
			break;
		case NUMERIC_NEG:
			printf("NEG");
			break;
		case NUMERIC_NAN:
			printf("NaN");
			break;
		case NUMERIC_PINF:
			printf("Infinity");
			break;
		case NUMERIC_NINF:
			printf("-Infinity");
			break;
		default:
			printf("SIGN=0x%x", NUMERIC_SIGN(fc_num));
			break;
	}

	for (fc_i = 0; fc_i < fc_ndigits; fc_i++)
		printf(" %0*d", DEC_DIGITS, fc_digits[fc_i]);
	printf("\n");
}


/*
 * dump_var() - 以变量格式转储值以进行调试
 */
static void fc_dump_var(const char *fc_str, NumericVar *fc_var)
{
	int			fc_i;

	printf("%s: VAR w=%d d=%d ", fc_str, fc_var->weight, fc_var->dscale);
	switch (fc_var->sign)
	{
		case NUMERIC_POS:
			printf("POS");
			break;
		case NUMERIC_NEG:
			printf("NEG");
			break;
		case NUMERIC_NAN:
			printf("NaN");
			break;
		case NUMERIC_PINF:
			printf("Infinity");
			break;
		case NUMERIC_NINF:
			printf("-Infinity");
			break;
		default:
			printf("SIGN=0x%x", fc_var->sign);
			break;
	}

	for (fc_i = 0; fc_i < fc_var->ndigits; fc_i++)
		printf(" %0*d", DEC_DIGITS, fc_var->digits[fc_i]);

	printf("\n");
}
#endif							/* NUMERIC_DEBUG */


/* ----------------------------------------------------------------------
 *
 * 本地函数如下
 *
 * 一般来说，这些不支持“特殊”（NaN或无穷）输入；
 * 调用者应首先处理这些情况。
 * （有一两个例外，在它们的头部注释中说明。）
 *
 * ----------------------------------------------------------------------
 */


/*
 * alloc_var() -
 *
 *	分配一个ndigits位数的数字缓冲区（加上一个备用数字用于舍入）
 */
static void fc_alloc_var(NumericVar *fc_var, int fc_ndigits)
{
	digitbuf_free(fc_var->buf);
	fc_var->buf = digitbuf_alloc(fc_ndigits + 1);
	fc_var->buf[0] = 0;			/* 用于舍入的备用数字 */
	fc_var->digits = fc_var->buf + 1;
	fc_var->ndigits = fc_ndigits;
}


/*
 * free_var() -
 *
 *	将变量的数字缓冲区返回到空闲池
 */
static void fc_free_var(NumericVar *fc_var)
{
	digitbuf_free(fc_var->buf);
	fc_var->buf = NULL;
	fc_var->digits = NULL;
	fc_var->sign = NUMERIC_NAN;
}


/*
 * zero_var() -
 *
 *	将变量设置为零。
 *	注意：其dscale不被触动。
 */
static void fc_zero_var(NumericVar *fc_var)
{
	digitbuf_free(fc_var->buf);
	fc_var->buf = NULL;
	fc_var->digits = NULL;
	fc_var->ndigits = 0;
	fc_var->weight = 0;			/* 按惯例；实际上没有关系 */
	fc_var->sign = NUMERIC_POS;	/* 除了NAN之外的任何东西... */
}


/*
 * set_var_from_str()
 *
 *	解析字符串并将数字放入变量中。
 *
 * 此函数不处理前导或尾随空格。它返回解析的
 * 结束+1位置，因此调用者可以在认为必要时检查尾随
 * 空格/垃圾。
 *
 * cp是实际开始解析的地方；str是用于错误
 * 报告的内容。（通常cp会相同，但会在空格后提前。）
 */
static const char * set_var_from_str(const char *fc_str, const char *fc_cp, NumericVar *fc_dest)
{
	bool		fc_have_dp = false;
	int			fc_i;
	unsigned char *fc_decdigits;
	int			fc_sign = NUMERIC_POS;
	int			fc_dweight = -1;
	int			fc_ddigits;
	int			fc_dscale = 0;
	int			fc_weight;
	int			fc_ndigits;
	int			fc_offset;
	NumericDigit *fc_digits;

	/*
	 * 我们首先解析字符串以提取十进制数字并确定
	 * 正确的十进制权重。然后转换为NBASE表示。
	 */
	switch (*fc_cp)
	{
		case '+':
			fc_sign = NUMERIC_POS;
			fc_cp++;
			break;

		case '-':
			fc_sign = NUMERIC_NEG;
			fc_cp++;
			break;
	}

	if (*fc_cp == '.')
	{
		fc_have_dp = true;
		fc_cp++;
	}

	if (!isdigit((unsigned char) *fc_cp))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"numeric", fc_str)));

	fc_decdigits = (unsigned char *) palloc(strlen(fc_cp) + DEC_DIGITS * 2);

	/* 用于后续数字对齐的前导填充 */
	memset(fc_decdigits, 0, DEC_DIGITS);
	fc_i = DEC_DIGITS;

	while (*fc_cp)
	{
		if (isdigit((unsigned char) *fc_cp))
		{
			fc_decdigits[fc_i++] = *fc_cp++ - '0';
			if (!fc_have_dp)
				fc_dweight++;
			else
				fc_dscale++;
		}
		else if (*fc_cp == '.')
		{
			if (fc_have_dp)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("invalid input syntax for type %s: \"%s\"",
								"numeric", fc_str)));
			fc_have_dp = true;
			fc_cp++;
		}
		else
			break;
	}

	fc_ddigits = fc_i - DEC_DIGITS;
	/* 用于后续数字对齐的尾随填充 */
	memset(fc_decdigits + fc_i, 0, DEC_DIGITS - 1);

	/* 处理指数（如果有的话） */
	if (*fc_cp == 'e' || *fc_cp == 'E')
	{
		long		fc_exponent;
		char	   *fc_endptr;

		fc_cp++;
		fc_exponent = strtol(fc_cp, &fc_endptr, 10);
		if (fc_endptr == fc_cp)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"numeric", fc_str)));
		fc_cp = fc_endptr;

		/*
		 * 此时，由于MaxAllocSize限制字符串长度，dweight和dscale不能超过约
		 * INT_MAX/2，因此对指数进行类似约束应该足够防止
		 * 这个函数中的整数溢出。如果值太大而无法
		 * 适应存储格式，make_result()将在稍后对此进行投诉；
		 * 为了一致性，使用与make_result()相同的ereport错误代码/文本。
		 */
		if (fc_exponent >= INT_MAX / 2 || fc_exponent <= -(INT_MAX / 2))
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value overflows numeric format")));
		fc_dweight += (int) fc_exponent;
		fc_dscale -= (int) fc_exponent;
		if (fc_dscale < 0)
			fc_dscale = 0;
	}

	
	if (fc_dweight >= 0)
		fc_weight = (fc_dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1;
	else
		fc_weight = -((-fc_dweight - 1) / DEC_DIGITS + 1);
	fc_offset = (fc_weight + 1) * DEC_DIGITS - (fc_dweight + 1);
	fc_ndigits = (fc_ddigits + fc_offset + DEC_DIGITS - 1) / DEC_DIGITS;

	fc_alloc_var(fc_dest, fc_ndigits);
	fc_dest->sign = fc_sign;
	fc_dest->weight = fc_weight;
	fc_dest->dscale = fc_dscale;

	fc_i = DEC_DIGITS - fc_offset;
	fc_digits = fc_dest->digits;

	while (fc_ndigits-- > 0)
	{
#if DEC_DIGITS == 4
		*fc_digits++ = ((fc_decdigits[fc_i] * 10 + fc_decdigits[fc_i + 1]) * 10 +
					 fc_decdigits[fc_i + 2]) * 10 + fc_decdigits[fc_i + 3];
#elif DEC_DIGITS == 2
		*digits++ = decdigits[i] * 10 + decdigits[i + 1];
#elif DEC_DIGITS == 1
		*digits++ = decdigits[i];
#else
#error unsupported NBASE
#endif
		fc_i += DEC_DIGITS;
	}

	pfree(fc_decdigits);

	/* 去除任何前导/后导零，并在零的情况下规范化权重 */
	fc_strip_var(fc_dest);

	/* 返回给调用者的结束+1位置 */
	return fc_cp;
}


/*
 * set_var_from_num() -
 *
 *	将打包的数据库格式转换为变量
 */
static void set_var_from_num(Numeric fc_num, NumericVar *fc_dest)
{
	int			fc_ndigits;

	fc_ndigits = NUMERIC_NDIGITS(fc_num);

	fc_alloc_var(fc_dest, fc_ndigits);

	fc_dest->weight = NUMERIC_WEIGHT(fc_num);
	fc_dest->sign = NUMERIC_SIGN(fc_num);
	fc_dest->dscale = NUMERIC_DSCALE(fc_num);

	memcpy(fc_dest->digits, NUMERIC_DIGITS(fc_num), fc_ndigits * sizeof(NumericDigit));
}


/*
 * init_var_from_num() -
 *
 *	从打包数据库格式初始化一个变量。数字数组不被复制，
 *	这在结果变量不被修改时节省了一些周期。
 *	此外，只要不将其他值赋给它（通过set_var_*函数，或将变量用作
 *	像add_var()这样的函数的目标），就无需调用free_var()。
 *
 *	注意：不要修改使用此函数初始化的变量的数字缓冲区，例如，调用
 *	round_var()或trunc_var()，因为更改会传播到原始Numeric！不过可以
 *	将其用作某些计算函数的目标参数。
 */
static void fc_init_var_from_num(Numeric fc_num, NumericVar *fc_dest)
{
	fc_dest->ndigits = NUMERIC_NDIGITS(fc_num);
	fc_dest->weight = NUMERIC_WEIGHT(fc_num);
	fc_dest->sign = NUMERIC_SIGN(fc_num);
	fc_dest->dscale = NUMERIC_DSCALE(fc_num);
	fc_dest->digits = NUMERIC_DIGITS(fc_num);
	fc_dest->buf = NULL;			/* 数字数组未使用palloc分配 */
}


/*
 * set_var_from_var() -
 *
 *	将一个变量复制到另一个变量
 */
static void set_var_from_var(const NumericVar *fc_value, NumericVar *fc_dest)
{
	NumericDigit *fc_newbuf;

	fc_newbuf = digitbuf_alloc(fc_value->ndigits + 1);
	fc_newbuf[0] = 0;				/* 用于舍入的备用数字 */
	if (fc_value->ndigits > 0)		/* 否则value->digits可能为null */
		memcpy(fc_newbuf + 1, fc_value->digits,
			   fc_value->ndigits * sizeof(NumericDigit));

	digitbuf_free(fc_dest->buf);

	memmove(fc_dest, fc_value, sizeof(NumericVar));
	fc_dest->buf = fc_newbuf;
	fc_dest->digits = fc_newbuf + 1;
}


/*
 * get_str_from_var() -
 *
 *	将变量转换为文本表示（numeric_out的核心）。
 *	变量以其dscale指示的数字显示。
 *	返回一个palloc分配的字符串。
 */
static char * get_str_from_var(const NumericVar *fc_var)
{
	int			fc_dscale;
	char	   *fc_str;
	char	   *fc_cp;
	char	   *fc_endcp;
	int			fc_i;
	int			fc_d;
	NumericDigit fc_dig;

#if DEC_DIGITS > 1
	NumericDigit fc_d1;
#endif

	fc_dscale = fc_var->dscale;

	/*
	 * 为结果分配空间。
	 *
	 * i被设置为小数点前的十进制数字数。dscale是我们将在小数点后打印的
	 * 十进制数字数。我们可能会在末尾生成多达DEC_DIGITS-1个多余数字，
	 * 另外我们还需要空间来放置符号、小数点、空终止符。
	 */
	fc_i = (fc_var->weight + 1) * DEC_DIGITS;
	if (fc_i <= 0)
		fc_i = 1;

	fc_str = palloc(fc_i + fc_dscale + DEC_DIGITS + 2);
	fc_cp = fc_str;

	/*
	 * 为负值输出一个负号
	 */
	if (fc_var->sign == NUMERIC_NEG)
		*fc_cp++ = '-';

	/*
	 * 输出小数点前的所有数字
	 */
	if (fc_var->weight < 0)
	{
		fc_d = fc_var->weight + 1;
		*fc_cp++ = '0';
	}
	else
	{
		for (fc_d = 0; fc_d <= fc_var->weight; fc_d++)
		{
			fc_dig = (fc_d < fc_var->ndigits) ? fc_var->digits[fc_d] : 0;
			/* 在第一个数字中，抑制额外的前导小数零 */
#if DEC_DIGITS == 4
			{
				bool		fc_putit = (fc_d > 0);

				fc_d1 = fc_dig / 1000;
				fc_dig -= fc_d1 * 1000;
				fc_putit |= (fc_d1 > 0);
				if (fc_putit)
					*fc_cp++ = fc_d1 + '0';
				fc_d1 = fc_dig / 100;
				fc_dig -= fc_d1 * 100;
				fc_putit |= (fc_d1 > 0);
				if (fc_putit)
					*fc_cp++ = fc_d1 + '0';
				fc_d1 = fc_dig / 10;
				fc_dig -= fc_d1 * 10;
				fc_putit |= (fc_d1 > 0);
				if (fc_putit)
					*fc_cp++ = fc_d1 + '0';
				*fc_cp++ = fc_dig + '0';
			}
#elif DEC_DIGITS == 2
			d1 = dig / 10;
			dig -= d1 * 10;
			if (d1 > 0 || d > 0)
				*cp++ = d1 + '0';
			*cp++ = dig + '0';
#elif DEC_DIGITS == 1
			*cp++ = dig + '0';
#else
#error unsupported NBASE
#endif
		}
	}

	/*
	 * 如果要求，输出一个小数点以及后面所有数字。
	 * 我们最初会输出一个DEC_DIGITS的倍数的数字，然后在
	 * 需要时截断。
	 */
	if (fc_dscale > 0)
	{
		*fc_cp++ = '.';
		fc_endcp = fc_cp + fc_dscale;
		for (fc_i = 0; fc_i < fc_dscale; fc_d++, fc_i += DEC_DIGITS)
		{
			fc_dig = (fc_d >= 0 && fc_d < fc_var->ndigits) ? fc_var->digits[fc_d] : 0;
#if DEC_DIGITS == 4
			fc_d1 = fc_dig / 1000;
			fc_dig -= fc_d1 * 1000;
			*fc_cp++ = fc_d1 + '0';
			fc_d1 = fc_dig / 100;
			fc_dig -= fc_d1 * 100;
			*fc_cp++ = fc_d1 + '0';
			fc_d1 = fc_dig / 10;
			fc_dig -= fc_d1 * 10;
			*fc_cp++ = fc_d1 + '0';
			*fc_cp++ = fc_dig + '0';
#elif DEC_DIGITS == 2
			d1 = dig / 10;
			dig -= d1 * 10;
			*cp++ = d1 + '0';
			*cp++ = dig + '0';
#elif DEC_DIGITS == 1
			*cp++ = dig + '0';
#else
#error unsupported NBASE
#endif
		}
		fc_cp = fc_endcp;
	}

	/*
	 * 终止字符串并返回
	 */
	*fc_cp = '\0';
	return fc_str;
}

/*
 * get_str_from_var_sci() -
 *
 *	将变量转换为规范化的科学记数法文本表示。
 *	此函数为numeric_out_sci()提供了主要功能。
 *
 *	这种记数法的一般形式为a * 10^b，其中a称为“有效数字”，b
 *	称为“指数”。
 *
 *	由于我们无法在ASCII中进行上标（并且我们想复制printf的行为），
 *	我们使用E表示法显示指数，最少有两个指数数字。
 *
 *	例如，值1234可以输出为1.2e+03。
 *
 *	我们假设指数可以装入int32。
 *
 *	rscale是所需的小数点后十进制数字的数量，负值将被视为零。
 *
 *	返回一个palloc分配的字符串。
 */
static char * get_str_from_var_sci(const NumericVar *fc_var, int fc_rscale)
{
	int32		fc_exponent;
	NumericVar	fc_tmp_var;
	size_t		fc_len;
	char	   *fc_str;
	char	   *fc_sig_out;

	if (fc_rscale < 0)
		fc_rscale = 0;

	/*
	 * 确定这个数字在规范化形式下的指数。
	 *
	 * 这是表示数字仅具有一个有效数字在小数点前的所需指数。
	 */
	if (fc_var->ndigits > 0)
	{
		fc_exponent = (fc_var->weight + 1) * DEC_DIGITS;

		/*
		 * 通过递减指数来补偿第一个数字中的前导小数零。
		 */
		fc_exponent -= DEC_DIGITS - (int) log10(fc_var->digits[0]);
	}
	else
	{
		/*
		 * 如果变量没有数字，则它必须是零。
		 *
		 * 零在规范化记数法中技术上没有有意义的指数，
		 * 但为了输出的一致性，我们将指数显示为零。
		 */
		fc_exponent = 0;
	}

	/*
	 * 将变量除以10^指数以获得有效数字，同时在此过程中
	 * 四舍五入到rscale个小数位。
	 */
	init_var(&fc_tmp_var);

	fc_power_ten_int(fc_exponent, &fc_tmp_var);
	fc_div_var(fc_var, &fc_tmp_var, &fc_tmp_var, fc_rscale, true);
	fc_sig_out = get_str_from_var(&fc_tmp_var);

	fc_free_var(&fc_tmp_var);

	/*
	 * 为结果分配空间。
	 *
	 * 除了有效数字，我们还需要空间放置指数装饰（“e”），
	 * 指数的符号，最多10位的指数本身，当然还有空终止符。
	 */
	fc_len = strlen(fc_sig_out) + 13;
	fc_str = palloc(fc_len);
	snprintf(fc_str, fc_len, "%se%+03d", fc_sig_out, fc_exponent);

	pfree(fc_sig_out);

	return fc_str;
}


/*
 * numericvar_serialize - 将NumericVar序列化为二进制格式
 *
 * 在变量级别上，不对权重或dscale进行检查，允许我们传递
 * 比数字类型支持的更高精度的中间值。 注意：这与numeric_send/recv()
 * 不兼容，它们对这些字段使用16位整数。
 */
static void fc_numericvar_serialize(StringInfo fc_buf, const NumericVar *fc_var)
{
	int			fc_i;

	pq_sendint32(fc_buf, fc_var->ndigits);
	pq_sendint32(fc_buf, fc_var->weight);
	pq_sendint32(fc_buf, fc_var->sign);
	pq_sendint32(fc_buf, fc_var->dscale);
	for (fc_i = 0; fc_i < fc_var->ndigits; fc_i++)
		pq_sendint16(fc_buf, fc_var->digits[fc_i]);
}

/*
 * numericvar_deserialize - 将二进制格式反序列化为NumericVar
 */
static void fc_numericvar_deserialize(StringInfo fc_buf, NumericVar *fc_var)
{
	int			fc_len,
				fc_i;

	fc_len = pq_getmsgint(fc_buf, sizeof(int32));

	fc_alloc_var(fc_var, fc_len);		/* 设置var->ndigits */

	fc_var->weight = pq_getmsgint(fc_buf, sizeof(int32));
	fc_var->sign = pq_getmsgint(fc_buf, sizeof(int32));
	fc_var->dscale = pq_getmsgint(fc_buf, sizeof(int32));
	for (fc_i = 0; fc_i < fc_len; fc_i++)
		fc_var->digits[fc_i] = pq_getmsgint(fc_buf, sizeof(int16));
}


/*
 * duplicate_numeric() - 复制打包格式的Numeric
 *
 * 这将处理NaN和无穷大情况。
 */
static Numeric fc_duplicate_numeric(Numeric fc_num)
{
	Numeric		fc_res;

	fc_res = (Numeric) palloc(VARSIZE(fc_num));
	memcpy(fc_res, fc_num, VARSIZE(fc_num));
	return fc_res;
}

/*
 * make_result_opt_error() -
 *
 *	从变量中创建palloc()分配内存的打包数据库数字格式。
 *	这将处理NaN和无穷大情况。
 *
 *	如果“have_error”不为NULL，则在溢出时将*have_error设置为true
 *	并返回NULL。这在调用者需要处理错误时非常有用。
 */
static Numeric fc_make_result_opt_error(const NumericVar *fc_var, bool *fc_have_error)
{
	Numeric		fc_result;
	NumericDigit *fc_digits = fc_var->digits;
	int			fc_weight = fc_var->weight;
	int			fc_sign = fc_var->sign;
	int			fc_n;
	Size		fc_len;

	if (fc_have_error)
		*fc_have_error = false;

	if ((fc_sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL)
	{
		/*
		 * 验证有效的特殊值。这可能只是一个断言，
		 * 但似乎值得花费一些周期来确保我们
		 * 永远不会将任何非零的保留位写入磁盘。
		 */
		if (!(fc_sign == NUMERIC_NAN ||
			  fc_sign == NUMERIC_PINF ||
			  fc_sign == NUMERIC_NINF))
			elog(ERROR, "invalid numeric sign value 0x%x", fc_sign);

		fc_result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);

		SET_VARSIZE(fc_result, NUMERIC_HDRSZ_SHORT);
		fc_result->choice.n_header = fc_sign;
		/* 头部字是我们所需的所有内容 */

		fc_dump_numeric("make_result()", fc_result);
		return fc_result;
	}

	fc_n = fc_var->ndigits;

	/* 截断前导零 */
	while (fc_n > 0 && *fc_digits == 0)
	{
		fc_digits++;
		fc_weight--;
		fc_n--;
	}
	/* 截断后导零 */
	while (fc_n > 0 && fc_digits[fc_n - 1] == 0)
		fc_n--;

	/* 如果结果为零，强制设置权重=0和正符号 */
	if (fc_n == 0)
	{
		fc_weight = 0;
		fc_sign = NUMERIC_POS;
	}

	/* 构建结果 */
	if (NUMERIC_CAN_BE_SHORT(fc_var->dscale, fc_weight))
	{
		fc_len = NUMERIC_HDRSZ_SHORT + fc_n * sizeof(NumericDigit);
		fc_result = (Numeric) palloc(fc_len);
		SET_VARSIZE(fc_result, fc_len);
		fc_result->choice.n_short.n_header =
			(fc_sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK)
			 : NUMERIC_SHORT)
			| (fc_var->dscale << NUMERIC_SHORT_DSCALE_SHIFT)
			| (fc_weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0)
			| (fc_weight & NUMERIC_SHORT_WEIGHT_MASK);
	}
	else
	{
		fc_len = NUMERIC_HDRSZ + fc_n * sizeof(NumericDigit);
		fc_result = (Numeric) palloc(fc_len);
		SET_VARSIZE(fc_result, fc_len);
		fc_result->choice.n_long.n_sign_dscale =
			fc_sign | (fc_var->dscale & NUMERIC_DSCALE_MASK);
		fc_result->choice.n_long.n_weight = fc_weight;
	}

	Assert(NUMERIC_NDIGITS(fc_result) == fc_n);
	if (fc_n > 0)
		memcpy(NUMERIC_DIGITS(fc_result), fc_digits, fc_n * sizeof(NumericDigit));

	
/* 检查 int16 字段的溢出 */
	if (NUMERIC_WEIGHT(fc_result) != fc_weight ||
		NUMERIC_DSCALE(fc_result) != fc_var->dscale)
	{
		if (fc_have_error)
		{
			*fc_have_error = true;
			return NULL;
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value overflows numeric format")));
		}
	}

	fc_dump_numeric("make_result()", fc_result);
	return fc_result;
}


/*
 * make_result() -
 *
 *  一个接口，用于在没有“have_error”参数的情况下调用 make_result_opt_error()。
 */
static Numeric fc_make_result(const NumericVar *fc_var)
{
	return fc_make_result_opt_error(fc_var, NULL);
}


/******
 * apply_typmod() -
 *
 *  根据指定的 typmod 进行边界检查和舍入。
 *  请注意，这仅适用于正常的有限值。
 */
static void fc_apply_typmod(NumericVar *fc_var, int32 fc_typmod)
{
	int			fc_precision;
	int			fc_scale;
	int			fc_maxdigits;
	int			fc_ddigits;
	int			fc_i;

	/* 如果 typmod 无效，则不执行任何操作 */
	if (!fc_is_valid_numeric_typmod(fc_typmod))
		return;

	fc_precision = fc_numeric_typmod_precision(fc_typmod);
	fc_scale = fc_numeric_typmod_scale(fc_typmod);
	fc_maxdigits = fc_precision - fc_scale;

	/* 向目标刻度舍入（并设置 var->dscale） */
	fc_round_var(fc_var, fc_scale);

	/* 但不允许 var->dscale 为负 */
	if (fc_var->dscale < 0)
		fc_var->dscale = 0;

	/*
	 * 检查溢出 - 请注意，我们无法在舍入之前进行此检查，
	 * 因为舍入可能会提高权重。同样请注意，变量的权重可能会
	 * 被前导零膨胀，这些前导零在存储之前会被去掉，
	 * 但也许现在还没有去掉。无论如何，我们必须识别一个
	 * 真正的零，其权重没有任何意义。
	 */
	fc_ddigits = (fc_var->weight + 1) * DEC_DIGITS;
	if (fc_ddigits > fc_maxdigits)
	{
		/* 确定真实权重；并检查全零结果 */
		for (fc_i = 0; fc_i < fc_var->ndigits; fc_i++)
		{
			NumericDigit fc_dig = fc_var->digits[fc_i];

			if (fc_dig)
			{
				/* 调整任何高位十进制零位 */
#if DEC_DIGITS == 4
				if (fc_dig < 10)
					fc_ddigits -= 3;
				else if (fc_dig < 100)
					fc_ddigits -= 2;
				else if (fc_dig < 1000)
					fc_ddigits -= 1;
#elif DEC_DIGITS == 2
				if (dig < 10)
					ddigits -= 1;
#elif DEC_DIGITS == 1
				/* 无需调整 */
#else
#error unsupported NBASE
#endif
				if (fc_ddigits > fc_maxdigits)
					ereport(ERROR,
							(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
							 errmsg("numeric field overflow"),
							 errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.",
									   fc_precision, fc_scale,
					/* 将 10^0 显示为 1 */
									   fc_maxdigits ? "10^" : "",
									   fc_maxdigits ? fc_maxdigits : 1
									   )));
				break;
			}
			fc_ddigits -= DEC_DIGITS;
		}
	}
}

/*
 * apply_typmod_special() -
 *
 *  根据指定的 typmod 进行边界检查，适用于 Inf 或 NaN。
 *  为了方便大多数调用者，值以打包形式呈现。
 */
static void fc_apply_typmod_special(Numeric fc_num, int32 fc_typmod)
{
	int			fc_precision;
	int			fc_scale;

	Assert(NUMERIC_IS_SPECIAL(fc_num));	/* 如果没有，则为调用者错误 */

	/*
	 * NaN 是被允许的，无论 typmod 如何；这可能有些可疑，
	 * 但这是一种长期存在的行为。如果我们有任何 typmod
	 * 限制，则会拒绝 Inf，因为不应声称无穷大适合于
	 * 任何有限的数字位数。
	 */
	if (NUMERIC_IS_NAN(fc_num))
		return;

	/* 如果我们有默认的 typmod (-1)，则不执行任何操作 */
	if (!fc_is_valid_numeric_typmod(fc_typmod))
		return;

	fc_precision = fc_numeric_typmod_precision(fc_typmod);
	fc_scale = fc_numeric_typmod_scale(fc_typmod);

	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("numeric field overflow"),
			 errdetail("A field with precision %d, scale %d cannot hold an infinite value.",
					   fc_precision, fc_scale)));
}


/*
 * 将数字转换为 int8，必要时进行舍入。
 *
 * 如果溢出，返回 false（不会引发错误）。如果正常，返回 true。
 */
static bool fc_numericvar_to_int64(const NumericVar *fc_var, int64 *fc_result)
{
	NumericDigit *fc_digits;
	int			fc_ndigits;
	int			fc_weight;
	int			fc_i;
	int64		fc_val;
	bool		fc_neg;
	NumericVar	fc_rounded;

	/* 向最近整数舍入 */
	init_var(&fc_rounded);
	set_var_from_var(fc_var, &fc_rounded);
	fc_round_var(&fc_rounded, 0);

	/* 检查零输入 */
	fc_strip_var(&fc_rounded);
	fc_ndigits = fc_rounded.ndigits;
	if (fc_ndigits == 0)
	{
		*fc_result = 0;
		fc_free_var(&fc_rounded);
		return true;
	}

	/*
	 * 对于像 10000000000 这样的输入，我们必须将去掉的数字视为真实数字。
	 * 因此，循环假设小数点前有 weight+1 位数字。
	 */
	fc_weight = fc_rounded.weight;
	Assert(fc_weight >= 0 && fc_ndigits <= fc_weight + 1);

	/*
	 * 构造结果。为了避免转换一个值
	 * 对应于 INT64_MIN（无法表示为正的 64
	 * 位二进制补码整数）时出现问题，累计值作为负数。
	 */
	fc_digits = fc_rounded.digits;
	fc_neg = (fc_rounded.sign == NUMERIC_NEG);
	fc_val = -fc_digits[0];
	for (fc_i = 1; fc_i <= fc_weight; fc_i++)
	{
		if (unlikely(pg_mul_s64_overflow(fc_val, NBASE, &fc_val)))
		{
			fc_free_var(&fc_rounded);
			return false;
		}

		if (fc_i < fc_ndigits)
		{
			if (unlikely(pg_sub_s64_overflow(fc_val, fc_digits[fc_i], &fc_val)))
			{
				fc_free_var(&fc_rounded);
				return false;
			}
		}
	}

	fc_free_var(&fc_rounded);

	if (!fc_neg)
	{
		if (unlikely(fc_val == PG_INT64_MIN))
			return false;
		fc_val = -fc_val;
	}
	*fc_result = fc_val;

	return true;
}

/*
 * 将 int8 值转换为数字。
 */
static void fc_int64_to_numericvar(int64 fc_val, NumericVar *fc_var)
{
	uint64		fc_uval,
				fc_newuval;
	NumericDigit *fc_ptr;
	int			fc_ndigits;

	/* int64 最多可以需要 19 位小数；出于安全考虑，加一 */
	fc_alloc_var(fc_var, 20 / DEC_DIGITS);
	if (fc_val < 0)
	{
		fc_var->sign = NUMERIC_NEG;
		fc_uval = -fc_val;
	}
	else
	{
		fc_var->sign = NUMERIC_POS;
		fc_uval = fc_val;
	}
	fc_var->dscale = 0;
	if (fc_val == 0)
	{
		fc_var->ndigits = 0;
		fc_var->weight = 0;
		return;
	}
	fc_ptr = fc_var->digits + fc_var->ndigits;
	fc_ndigits = 0;
	do
	{
		fc_ptr--;
		fc_ndigits++;
		fc_newuval = fc_uval / NBASE;
		*fc_ptr = fc_uval - fc_newuval * NBASE;
		fc_uval = fc_newuval;
	} while (fc_uval);
	fc_var->digits = fc_ptr;
	fc_var->ndigits = fc_ndigits;
	fc_var->weight = fc_ndigits - 1;
}

/*
 * 将数字转换为 uint64，必要时进行舍入。
 *
 * 如果溢出，返回 false（不会引发错误）。如果正常，返回 true。
 */
static bool fc_numericvar_to_uint64(const NumericVar *fc_var, uint64 *fc_result)
{
	NumericDigit *fc_digits;
	int			fc_ndigits;
	int			fc_weight;
	int			fc_i;
	uint64		fc_val;
	NumericVar	fc_rounded;

	/* 向最近整数舍入 */
	init_var(&fc_rounded);
	set_var_from_var(fc_var, &fc_rounded);
	fc_round_var(&fc_rounded, 0);

	/* 检查零输入 */
	fc_strip_var(&fc_rounded);
	fc_ndigits = fc_rounded.ndigits;
	if (fc_ndigits == 0)
	{
		*fc_result = 0;
		fc_free_var(&fc_rounded);
		return true;
	}

	/* 检查负输入 */
	if (fc_rounded.sign == NUMERIC_NEG)
	{
		fc_free_var(&fc_rounded);
		return false;
	}

	/*
	 * 对于像 10000000000 这样的输入，我们必须将去掉的数字视为真实数字。
	 * 因此，循环假设小数点前有 weight+1 位数字。
	 */
	fc_weight = fc_rounded.weight;
	Assert(fc_weight >= 0 && fc_ndigits <= fc_weight + 1);

	/* 构造结果 */
	fc_digits = fc_rounded.digits;
	fc_val = fc_digits[0];
	for (fc_i = 1; fc_i <= fc_weight; fc_i++)
	{
		if (unlikely(pg_mul_u64_overflow(fc_val, NBASE, &fc_val)))
		{
			fc_free_var(&fc_rounded);
			return false;
		}

		if (fc_i < fc_ndigits)
		{
			if (unlikely(pg_add_u64_overflow(fc_val, fc_digits[fc_i], &fc_val)))
			{
				fc_free_var(&fc_rounded);
				return false;
			}
		}
	}

	fc_free_var(&fc_rounded);

	*fc_result = fc_val;

	return true;
}

#ifdef HAVE_INT128
/*
 * 将数字转换为 int128，必要时进行舍入。
 *
 * 如果溢出，返回 false（不会引发错误）。如果正常，返回 true。
 */
static bool fc_numericvar_to_int128(const NumericVar *fc_var, int128 *fc_result)
{
	NumericDigit *fc_digits;
	int			fc_ndigits;
	int			fc_weight;
	int			fc_i;
	int128		fc_val,
				fc_oldval;
	bool		fc_neg;
	NumericVar	fc_rounded;

	/* 向最近整数舍入 */
	init_var(&fc_rounded);
	set_var_from_var(fc_var, &fc_rounded);
	fc_round_var(&fc_rounded, 0);

	/* 检查零输入 */
	fc_strip_var(&fc_rounded);
	fc_ndigits = fc_rounded.ndigits;
	if (fc_ndigits == 0)
	{
		*fc_result = 0;
		fc_free_var(&fc_rounded);
		return true;
	}

	/*
	 * 对于像 10000000000 这样的输入，我们必须将去掉的数字视为真实数字。
	 * 因此，循环假设小数点前有 weight+1 位数字。
	 */
	fc_weight = fc_rounded.weight;
	Assert(fc_weight >= 0 && fc_ndigits <= fc_weight + 1);

	/* 构造结果 */
	fc_digits = fc_rounded.digits;
	fc_neg = (fc_rounded.sign == NUMERIC_NEG);
	fc_val = fc_digits[0];
	for (fc_i = 1; fc_i <= fc_weight; fc_i++)
	{
		fc_oldval = fc_val;
		fc_val *= NBASE;
		if (fc_i < fc_ndigits)
			fc_val += fc_digits[fc_i];

		/*
		 * 溢出检查有点棘手，因为我们想接受
		 * INT128_MIN，这会使正累加器溢出。
		 * 不过，我们可以很容易地检测到这种情况，因为 INT128_MIN 是
		 * 唯一一个对于 -val == val 的非零值（不管是在二进制补码机器上）。
		 */
		if ((fc_val / NBASE) != fc_oldval)	/* 可能溢出？ */
		{
			if (!fc_neg || (-fc_val) != fc_val || fc_val == 0 || fc_oldval < 0)
			{
				fc_free_var(&fc_rounded);
				return false;
			}
		}
	}

	fc_free_var(&fc_rounded);

	*fc_result = fc_neg ? -fc_val : fc_val;
	return true;
}

/*
 * 将 128 位整数转换为数字。
 */
static void fc_int128_to_numericvar(int128 fc_val, NumericVar *fc_var)
{
	uint128		fc_uval,
				fc_newuval;
	NumericDigit *fc_ptr;
	int			fc_ndigits;

	/* int128 最多可以需要 39 位小数；出于安全考虑，加一 */
	fc_alloc_var(fc_var, 40 / DEC_DIGITS);
	if (fc_val < 0)
	{
		fc_var->sign = NUMERIC_NEG;
		fc_uval = -fc_val;
	}
	else
	{
		fc_var->sign = NUMERIC_POS;
		fc_uval = fc_val;
	}
	fc_var->dscale = 0;
	if (fc_val == 0)
	{
		fc_var->ndigits = 0;
		fc_var->weight = 0;
		return;
	}
	fc_ptr = fc_var->digits + fc_var->ndigits;
	fc_ndigits = 0;
	do
	{
		fc_ptr--;
		fc_ndigits++;
		fc_newuval = fc_uval / NBASE;
		*fc_ptr = fc_uval - fc_newuval * NBASE;
		fc_uval = fc_newuval;
	} while (fc_uval);
	fc_var->digits = fc_ptr;
	fc_var->ndigits = fc_ndigits;
	fc_var->weight = fc_ndigits - 1;
}
#endif

/*
 * 将 NumericVar 转换为 float8；如果超出范围，则返回 +/- HUGE_VAL
 */
static double fc_numericvar_to_double_no_overflow(const NumericVar *fc_var)
{
	char	   *fc_tmp;
	double		fc_val;
	char	   *fc_endptr;

	fc_tmp = get_str_from_var(fc_var);

	/* 与 float8in 不同，我们忽略来自 strtod 的 ERANGE */
	fc_val = strtod(fc_tmp, &fc_endptr);
	if (*fc_endptr != '\0')
	{
		/* 静默编译器，避免对无用测试的抱怨 */
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"double precision", fc_tmp)));
	}

	pfree(fc_tmp);

	return fc_val;
}


/*
 * cmp_var() -
 *
 *	比较两个变量级别的值。我们假设零已经截断到没有数字。
 */
static int fc_cmp_var(const NumericVar *fc_var1, const NumericVar *fc_var2)
{
	return fc_cmp_var_common(fc_var1->digits, fc_var1->ndigits,
						  fc_var1->weight, fc_var1->sign,
						  fc_var2->digits, fc_var2->ndigits,
						  fc_var2->weight, fc_var2->sign);
}

/*
 * cmp_var_common() -
 *
 *	cmp_var()的主程序。这个函数可以被NumericVar和Numeric使用。
 */
static int fc_cmp_var_common(const NumericDigit *fc_var1digits, int fc_var1ndigits,
			   int fc_var1weight, int fc_var1sign,
			   const NumericDigit *fc_var2digits, int fc_var2ndigits,
			   int fc_var2weight, int fc_var2sign)
{
	if (fc_var1ndigits == 0)
	{
		if (fc_var2ndigits == 0)
			return 0;
		if (fc_var2sign == NUMERIC_NEG)
			return 1;
		return -1;
	}
	if (fc_var2ndigits == 0)
	{
		if (fc_var1sign == NUMERIC_POS)
			return 1;
		return -1;
	}

	if (fc_var1sign == NUMERIC_POS)
	{
		if (fc_var2sign == NUMERIC_NEG)
			return 1;
		return fc_cmp_abs_common(fc_var1digits, fc_var1ndigits, fc_var1weight,
							  fc_var2digits, fc_var2ndigits, fc_var2weight);
	}

	if (fc_var2sign == NUMERIC_POS)
		return -1;

	return fc_cmp_abs_common(fc_var2digits, fc_var2ndigits, fc_var2weight,
						  fc_var1digits, fc_var1ndigits, fc_var1weight);
}


/*
 * add_var() -
 *
 *	变量级别加法功能的完整版（处理符号）。结果也可能指向其中一个操作数而没有危险。
 */
static void add_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	/*
	 * 根据信号决定这两个变量该怎么做
	 */
	if (fc_var1->sign == NUMERIC_POS)
	{
		if (fc_var2->sign == NUMERIC_POS)
		{
			/*
			 * 两个都是正数 结果 = +(ABS(var1) + ABS(var2))
			 */
			add_abs(fc_var1, fc_var2, fc_result);
			fc_result->sign = NUMERIC_POS;
		}
		else
		{
			/*
			 * var1是正数，var2是负数 必须比较绝对值
			 */
			switch (fc_cmp_abs(fc_var1, fc_var2))
			{
				case 0:
					/* ----------
					 * ABS(var1) == ABS(var2)
					 * 结果 = 零
					 * ----------
					 */
					fc_zero_var(fc_result);
					fc_result->dscale = Max(fc_var1->dscale, fc_var2->dscale);
					break;

				case 1:
					/* ----------
					 * ABS(var1) > ABS(var2)
					 * 结果 = +(ABS(var1) - ABS(var2))
					 * ----------
					 */
					fc_sub_abs(fc_var1, fc_var2, fc_result);
					fc_result->sign = NUMERIC_POS;
					break;

				case -1:
					/* ----------
					 * ABS(var1) < ABS(var2)
					 * 结果 = -(ABS(var2) - ABS(var1))
					 * ----------
					 */
					fc_sub_abs(fc_var2, fc_var1, fc_result);
					fc_result->sign = NUMERIC_NEG;
					break;
			}
		}
	}
	else
	{
		if (fc_var2->sign == NUMERIC_POS)
		{
			/* ----------
			 * var1是负数，var2是正数
			 * 必须比较绝对值
			 * ----------
			 */
			switch (fc_cmp_abs(fc_var1, fc_var2))
			{
				case 0:
					/* ----------
					 * ABS(var1) == ABS(var2)
					 * 结果 = 零
					 * ----------
					 */
					fc_zero_var(fc_result);
					fc_result->dscale = Max(fc_var1->dscale, fc_var2->dscale);
					break;

				case 1:
					/* ----------
					 * ABS(var1) > ABS(var2)
					 * result = -(ABS(var1) - ABS(var2))
					 * ----------
					 */
					fc_sub_abs(fc_var1, fc_var2, fc_result);
					fc_result->sign = NUMERIC_NEG;
					break;

				case -1:
					/* ----------
					 * ABS(var1) < ABS(var2)
					 * result = +(ABS(var2) - ABS(var1))
					 * ----------
					 */
					fc_sub_abs(fc_var2, fc_var1, fc_result);
					fc_result->sign = NUMERIC_POS;
					break;
			}
		}
		else
		{
			/* ----------
			 * 两者都是负数
			 * result = -(ABS(var1) + ABS(var2))
			 * ----------
			 */
			add_abs(fc_var1, fc_var2, fc_result);
			fc_result->sign = NUMERIC_NEG;
		}
	}
}


/*
 * sub_var() -
 *
 *	变量级别减法功能的完整版（处理符号）。结果也可能指向其中一个操作数而没有危险。
 */
static void fc_sub_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	/*
	 * 根据信号决定这两个变量该怎么做
	 */
	if (fc_var1->sign == NUMERIC_POS)
	{
		if (fc_var2->sign == NUMERIC_NEG)
		{
			/* ----------
			 * var1 是正数，var2 是负数
			 * result = +(ABS(var1) + ABS(var2))
			 * ----------
			 */
			add_abs(fc_var1, fc_var2, fc_result);
			fc_result->sign = NUMERIC_POS;
		}
		else
		{
			/* ----------
			 * 两者都是正数
			 * 必须比较绝对值
			 * ----------
			 */
			switch (fc_cmp_abs(fc_var1, fc_var2))
			{
				case 0:
					/* ----------
					 * ABS(var1) == ABS(var2)
					 * 结果 = 零
					 * ----------
					 */
					fc_zero_var(fc_result);
					fc_result->dscale = Max(fc_var1->dscale, fc_var2->dscale);
					break;

				case 1:
					/* ----------
					 * ABS(var1) > ABS(var2)
					 * 结果 = +(ABS(var1) - ABS(var2))
					 * ----------
					 */
					fc_sub_abs(fc_var1, fc_var2, fc_result);
					fc_result->sign = NUMERIC_POS;
					break;

				case -1:
					/* ----------
					 * ABS(var1) < ABS(var2)
					 * 结果 = -(ABS(var2) - ABS(var1))
					 * ----------
					 */
					fc_sub_abs(fc_var2, fc_var1, fc_result);
					fc_result->sign = NUMERIC_NEG;
					break;
			}
		}
	}
	else
	{
		if (fc_var2->sign == NUMERIC_NEG)
		{
			/* ----------
			 * 两者都是负数
			 * 必须比较绝对值
			 * ----------
			 */
			switch (fc_cmp_abs(fc_var1, fc_var2))
			{
				case 0:
					/* ----------
					 * ABS(var1) == ABS(var2)
					 * 结果 = 零
					 * ----------
					 */
					fc_zero_var(fc_result);
					fc_result->dscale = Max(fc_var1->dscale, fc_var2->dscale);
					break;

				case 1:
					/* ----------
					 * ABS(var1) > ABS(var2)
					 * result = -(ABS(var1) - ABS(var2))
					 * ----------
					 */
					fc_sub_abs(fc_var1, fc_var2, fc_result);
					fc_result->sign = NUMERIC_NEG;
					break;

				case -1:
					/* ----------
					 * ABS(var1) < ABS(var2)
					 * result = +(ABS(var2) - ABS(var1))
					 * ----------
					 */
					fc_sub_abs(fc_var2, fc_var1, fc_result);
					fc_result->sign = NUMERIC_POS;
					break;
			}
		}
		else
		{
			/* ----------
			 * var1 是负数，var2 是正数
			 * result = -(ABS(var1) + ABS(var2))
			 * ----------
			 */
			add_abs(fc_var1, fc_var2, fc_result);
			fc_result->sign = NUMERIC_NEG;
		}
	}
}


/*
 * mul_var() -
 *
 *	变量级别的乘法。var1 * var2的乘积存储在结果中。结果四舍五入到不超过rscale的小数位数。
 */
static void fc_mul_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result,
		int fc_rscale)
{
	int			fc_res_ndigits;
	int			fc_res_sign;
	int			fc_res_weight;
	int			fc_maxdigits;
	int		   *fc_dig;
	int			fc_carry;
	int			fc_maxdig;
	int			fc_newdig;
	int			fc_var1ndigits;
	int			fc_var2ndigits;
	NumericDigit *fc_var1digits;
	NumericDigit *fc_var2digits;
	NumericDigit *fc_res_digits;
	int			fc_i,
				fc_i1,
				fc_i2;

	/*
	 * 确保var1是两个数字中较短的一个。这提高了性能，因为内部乘法循环比外部循环简单得多，因此最好减少外部循环的迭代次数。这也减少了需要规范化累加器数组的次数。
	 */
	if (fc_var1->ndigits > fc_var2->ndigits)
	{
		const NumericVar *fc_tmp = fc_var1;

		fc_var1 = fc_var2;
		fc_var2 = fc_tmp;
	}

	/* 将这些值复制到局部变量中，以提高内部循环的速度 */
	fc_var1ndigits = fc_var1->ndigits;
	fc_var2ndigits = fc_var2->ndigits;
	fc_var1digits = fc_var1->digits;
	fc_var2digits = fc_var2->digits;

	if (fc_var1ndigits == 0 || fc_var2ndigits == 0)
	{
		/* 一个或两个输入为零；结果也是 */
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/* 确定结果符号和（最大可能的）权重 */
	if (fc_var1->sign == fc_var2->sign)
		fc_res_sign = NUMERIC_POS;
	else
		fc_res_sign = NUMERIC_NEG;
	fc_res_weight = fc_var1->weight + fc_var2->weight + 2;

	/*
	 * 确定要计算的结果数字的数量。如果精确结果会有超过rscale的小数位数，则用MUL_GUARD_DIGITS保护数字截断计算，即忽略输入数字，这些数字只会对右边的数字做出贡献。（这将给出精确的四舍五入到rscale的答案，除非忽略位置的进位会通过超过MUL_GUARD_DIGITS位的进位传播。）
	 *
	 * 注意：精确计算不能产生超过var1ndigits + var2ndigits的位数，但我们分配一个额外的输出位，以防rscale驱动的四舍五入产生最高精确位的进位。
	 */
	fc_res_ndigits = fc_var1ndigits + fc_var2ndigits + 1;
	fc_maxdigits = fc_res_weight + 1 + (fc_rscale + DEC_DIGITS - 1) / DEC_DIGITS +
		MUL_GUARD_DIGITS;
	fc_res_ndigits = Min(fc_res_ndigits, fc_maxdigits);

	if (fc_res_ndigits < 3)
	{
		/* 所有输入数字将被忽略；所以结果为零 */
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/*
	 * 我们在一个“dig[]”的有符号整数数组中进行算术运算。由于INT_MAX明显大于NBASE*NBASE，这给我们腾出了空间，以避免立即规范化进位。
	 *
	 * maxdig跟踪任何dig[]条目的最大可能值；当这一值威胁超过INT_MAX时，我们花时间传播进位。此外，我们还需要确保在进位传播过程中不会发生溢出。进位值最多可以达到INT_MAX/NBASE，因此当数字威胁超过INT_MAX - INT_MAX/NBASE时，我们必须规范化。
	 *
	 * 为了避免maxdig本身的溢出，它实际上表示最大可能值除以NBASE-1，即，在循环的顶部可以知道没有dig[]条目超过maxdig * (NBASE-1)。
	 */
	fc_dig = (int *) palloc0(fc_res_ndigits * sizeof(int));
	fc_maxdig = 0;

	/*
	 * 如果var1的最低有效数字不直接贡献于我们计算的结果的前res_ndigits位，则应忽略这些位。
	 *
	 * var1的数字i1和var2的数字i2相乘并加到累加器数组的数字i1+i2+2中，因此我们只需要考虑那些i1 <= res_ndigits - 3的var1的位数。
	 */
	for (fc_i1 = Min(fc_var1ndigits - 1, fc_res_ndigits - 3); fc_i1 >= 0; fc_i1--)
	{
		NumericDigit fc_var1digit = fc_var1digits[fc_i1];

		if (fc_var1digit == 0)
			continue;

		/* 该规范化时间到了吗？ */
		fc_maxdig += fc_var1digit;
		if (fc_maxdig > (INT_MAX - INT_MAX / NBASE) / (NBASE - 1))
		{
			/* 是的，执行它 */
			fc_carry = 0;
			for (fc_i = fc_res_ndigits - 1; fc_i >= 0; fc_i--)
			{
				fc_newdig = fc_dig[fc_i] + fc_carry;
				if (fc_newdig >= NBASE)
				{
					fc_carry = fc_newdig / NBASE;
					fc_newdig -= fc_carry * NBASE;
				}
				else
					fc_carry = 0;
				fc_dig[fc_i] = fc_newdig;
			}
			Assert(fc_carry == 0);
			/* 重置maxdig以指示新的最坏情况 */
			fc_maxdig = 1 + fc_var1digit;
		}

		/*
		 * 将var2的适当倍数加到累加器中。
		 *
		 * 如上所述，如果var2的位数不贡献，则可以忽略，所以我们只包含i1+i2+2 < res_ndigits的数字。
		 *
		 * 这个内循环是乘法的性能瓶颈，因此我们希望它简单到足以能够自动矢量化。因此，尽管学校中的乘法建议从右到左处理数字，但按从左到右处理数字。由于我们在这个循环中没有传播进位，顺序并不重要。
		 */
		{
			int			fc_i2limit = Min(fc_var2ndigits, fc_res_ndigits - fc_i1 - 2);
			int		   *fc_dig_i1_2 = &fc_dig[fc_i1 + 2];

			for (fc_i2 = 0; fc_i2 < fc_i2limit; fc_i2++)
				fc_dig_i1_2[fc_i2] += fc_var1digit * fc_var2digits[fc_i2];
		}
	}

	/*
	 * 现在我们进行最终的进位传播以规范化结果，我们将其与将结果数字存储到输出中结合起来。请注意，这仍然是在全精度下进行的，并带有保护数字。
	 */
	fc_alloc_var(fc_result, fc_res_ndigits);
	fc_res_digits = fc_result->digits;
	fc_carry = 0;
	for (fc_i = fc_res_ndigits - 1; fc_i >= 0; fc_i--)
	{
		fc_newdig = fc_dig[fc_i] + fc_carry;
		if (fc_newdig >= NBASE)
		{
			fc_carry = fc_newdig / NBASE;
			fc_newdig -= fc_carry * NBASE;
		}
		else
			fc_carry = 0;
		fc_res_digits[fc_i] = fc_newdig;
	}
	Assert(fc_carry == 0);

	pfree(fc_dig);

	/*
	 * 最后，将结果四舍五入到请求的精度。
	 */
	fc_result->weight = fc_res_weight;
	fc_result->sign = fc_res_sign;

	/* 四舍五入到目标rscale（并设置结果->dscale） */
	fc_round_var(fc_result, fc_rscale);

	/* 去掉前导和尾随零 */
	fc_strip_var(fc_result);
}


/*
 * div_var() -
 *
 *	变量级别的除法。var1 / var2的商存储在结果中。商精确到rscale小数位数。如果round为true，则在rscale位数进行四舍五入；如果为false，则在该位数向零截断。
 */
static void fc_div_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result,
		int fc_rscale, bool fc_round)
{
	int			fc_div_ndigits;
	int			fc_res_ndigits;
	int			fc_res_sign;
	int			fc_res_weight;
	int			fc_carry;
	int			fc_borrow;
	int			fc_divisor1;
	int			fc_divisor2;
	NumericDigit *fc_dividend;
	NumericDigit *fc_divisor;
	NumericDigit *fc_res_digits;
	int			fc_i;
	int			fc_j;

	/* 将这些值复制到局部变量中，以提高内部循环的速度 */
	int			fc_var1ndigits = fc_var1->ndigits;
	int			fc_var2ndigits = fc_var2->ndigits;

	/*
	 * 首先进行零除法检查；我们不能交给一个未规范化的除数。
	 */
	if (fc_var2ndigits == 0 || fc_var2->digits[0] == 0)
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));

	/*
	 * 如果除数只有一位或两位数字，则委托给div_var_int()，它使用快速短除法。
	 */
	if (fc_var2ndigits <= 2)
	{
		int			fc_idivisor;
		int			fc_idivisor_weight;

		fc_idivisor = fc_var2->digits[0];
		fc_idivisor_weight = fc_var2->weight;
		if (fc_var2ndigits == 2)
		{
			fc_idivisor = fc_idivisor * NBASE + fc_var2->digits[1];
			fc_idivisor_weight--;
		}
		if (fc_var2->sign == NUMERIC_NEG)
			fc_idivisor = -fc_idivisor;

		fc_div_var_int(fc_var1, fc_idivisor, fc_idivisor_weight, fc_result, fc_rscale, fc_round);
		return;
	}

	/*
	 * 否则，执行完整的长除法。
	 */

	/* 检查结果是否为零 */
	if (fc_var1ndigits == 0)
	{
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	
/*
	 * 确定结果的符号、权重和需要计算的数字位数。
	 * 这里计算的权重是正确的，如果发出的商没有
	 * 前导零数字；否则 strip_var() 将会修正。
	 */
	if (fc_var1->sign == fc_var2->sign)
		fc_res_sign = NUMERIC_POS;
	else
		fc_res_sign = NUMERIC_NEG;
	fc_res_weight = fc_var1->weight - fc_var2->weight;
	/* 我们需要产生的准确结果数字位数： */
	fc_res_ndigits = fc_res_weight + 1 + (fc_rscale + DEC_DIGITS - 1) / DEC_DIGITS;
	/* ... 但至少要 1 */
	fc_res_ndigits = Max(fc_res_ndigits, 1);
	/* 如果需要四舍五入，那么计算多出一位以确保结果正确 */
	if (fc_round)
		fc_res_ndigits++;

	/*
	 * 工作被除数通常需要 res_ndigits + var2ndigits
	 * 位数，但至少要 var1ndigits 以便我们可以将 var1
	 * 完全加载到其中。（被除数空间中还会有一个额外的数字 dividend[0]，
	 * 但为了与 Knuth 的符号一致，我们不将其计入 div_ndigits。）
	 */
	fc_div_ndigits = fc_res_ndigits + fc_var2ndigits;
	fc_div_ndigits = Max(fc_div_ndigits, fc_var1ndigits);

	/*
	 * 我们需要一个工作区，其中要有足够的空间以容纳工作利息（div_ndigits + 1 位数字），以及可能被归一化的除数（var2ndigits 位数字）。在除数[0] 处有一个零，并且实际的除数数据放在除数[1 .. var2ndigits]中，也是很方便的。将位数字转移到工作区中还允许我们在开始主循环之前重新分配结果（可能与任一输入变量相同）。请注意，我们使用 palloc0 来确保除数[0]、被除数[0] 以及任何超出 var1ndigits 的额外被除数位置一开始都是 0。
	 */
	fc_dividend = (NumericDigit *)
		palloc0((fc_div_ndigits + fc_var2ndigits + 2) * sizeof(NumericDigit));
	fc_divisor = fc_dividend + (fc_div_ndigits + 1);
	memcpy(fc_dividend + 1, fc_var1->digits, fc_var1ndigits * sizeof(NumericDigit));
	memcpy(fc_divisor + 1, fc_var2->digits, fc_var2ndigits * sizeof(NumericDigit));

	/*
	 * 现在我们可以重新分配结果以容纳生成的商位数字。
	 */
	fc_alloc_var(fc_result, fc_res_ndigits);
	fc_res_digits = fc_result->digits;

	/*
	 * 完整的多位算法取自 Knuth 第二卷，算法 4.3.1D。
	 *
	 * 我们需要第一个除数位数字 >= NBASE / 2。如果不是，通过 "d" 因子同时对除数和被除数进行缩放，使其满足这个条件。（在上面分配被除数[0] 的原因是为了给这里可能出现的进位留出空间。）
	 */
	if (fc_divisor[1] < HALF_NBASE)
	{
		int			fc_d = NBASE / (fc_divisor[1] + 1);

		fc_carry = 0;
		for (fc_i = fc_var2ndigits; fc_i > 0; fc_i--)
		{
			fc_carry += fc_divisor[fc_i] * fc_d;
			fc_divisor[fc_i] = fc_carry % NBASE;
			fc_carry = fc_carry / NBASE;
		}
		Assert(fc_carry == 0);
		fc_carry = 0;
		/* 此时只有 var1ndigits 的被除数可能非零 */
		for (fc_i = fc_var1ndigits; fc_i >= 0; fc_i--)
		{
			fc_carry += fc_dividend[fc_i] * fc_d;
			fc_dividend[fc_i] = fc_carry % NBASE;
			fc_carry = fc_carry / NBASE;
		}
		Assert(fc_carry == 0);
		Assert(fc_divisor[1] >= HALF_NBASE);
	}
	/* 前两个除数位在主循环中反复使用 */
	fc_divisor1 = fc_divisor[1];
	fc_divisor2 = fc_divisor[2];

	/*
	 * 开始主循环。该循环的每次迭代通过将被除数[j .. j + var2ndigits] 除以除数来产生第 j 个商位数字；这基本上与常见的手动长除法程序相同。
	 */
	for (fc_j = 0; fc_j < fc_res_ndigits; fc_j++)
	{
		/* 从前两个被除数位估计商位数字 */
		int			fc_next2digits = fc_dividend[fc_j] * NBASE + fc_dividend[fc_j + 1];
		int			fc_qhat;

		/*
		 * 如果 next2digits 为 0，那么商位数字必须为 0，无需调整工作被除数。在处理被除数末尾的零时，尽早退出是值得的。
		 */
		if (fc_next2digits == 0)
		{
			fc_res_digits[fc_j] = 0;
			continue;
		}

		if (fc_dividend[fc_j] == fc_divisor1)
			fc_qhat = NBASE - 1;
		else
			fc_qhat = fc_next2digits / fc_divisor1;

		/*
		 * 如果商位数字过大，则进行调整。Knuth 证明在此步骤后，商位数字要么正确，要么仅大一（注意：在这里使用被除数[j + 2] 是可以的，因为我们知道除数的长度至少为 2）。
		 */
		while (fc_divisor2 * fc_qhat >
			   (fc_next2digits - fc_qhat * fc_divisor1) * NBASE + fc_dividend[fc_j + 2])
			fc_qhat--;

		/* 与上面相同，当商位数字为 0 时，无需做更多操作 */
		if (fc_qhat > 0)
		{
			NumericDigit *fc_dividend_j = &fc_dividend[fc_j];

			/*
			 * 将除数乘以 qhat，并从工作被除数中减去。此处将乘法和减法折叠在一起，注意到 qhat <= NBASE（因为它可能大一），因此中间结果 "tmp_result" 的范围在 [-NBASE^2, NBASE - 1] 之间，"borrow" 的范围在 [0, NBASE] 之间。
			 */
			fc_borrow = 0;
			for (fc_i = fc_var2ndigits; fc_i >= 0; fc_i--)
			{
				int			fc_tmp_result;

				fc_tmp_result = fc_dividend_j[fc_i] - fc_borrow - fc_divisor[fc_i] * fc_qhat;
				fc_borrow = (NBASE - 1 - fc_tmp_result) / NBASE;
				fc_dividend_j[fc_i] = fc_tmp_result + fc_borrow * NBASE;
			}

			/*
			 * 如果我们从最高被除数位中得到了借位，那么确实 qhat 是大一。修正它，并将除数加回以纠正工作被除数。（Knuth 证明这种情况仅大约发生 3/NBASE 的时间；因此，用小的 NBASE 测试此代码是个好主意，以确保这个部分能得到锻炼。）
			 */
			if (fc_borrow)
			{
				fc_qhat--;
				fc_carry = 0;
				for (fc_i = fc_var2ndigits; fc_i >= 0; fc_i--)
				{
					fc_carry += fc_dividend_j[fc_i] + fc_divisor[fc_i];
					if (fc_carry >= NBASE)
					{
						fc_dividend_j[fc_i] = fc_carry - NBASE;
						fc_carry = 1;
					}
					else
					{
						fc_dividend_j[fc_i] = fc_carry;
						fc_carry = 0;
					}
				}
				/* 在此处应发生进位以抵消上述借位 */
				Assert(fc_carry == 1);
			}
		}

		/* 我们已经完成了这个商位数字的计算 */
		fc_res_digits[fc_j] = fc_qhat;
	}

	pfree(fc_dividend);

	/*
	 * 最后，将结果四舍五入或截断到请求的精度。
	 */
	fc_result->weight = fc_res_weight;
	fc_result->sign = fc_res_sign;

	/* 四舍五入或截断到目标 rscale（并设置 result->dscale） */
	if (fc_round)
		fc_round_var(fc_result, fc_rscale);
	else
		fc_trunc_var(fc_result, fc_rscale);

	/* 去掉前导和尾随零 */
	fc_strip_var(fc_result);
}


/*
 * div_var_fast() -
 *
 *	它具有与 div_var 相同的 API，但使用 "FM" 库中的除法算法实现，而不是 Knuth 的学校书籍除法方法。这大大提高了速度，但可能产生不准确的结果，因为它有时必须向左传播舍入，因此我们永远不能完全确定我们是否准确知道请求的位数字。我们计算 DIV_GUARD_DIGITS 额外的位数字，但没有保证这足够。我们仅在超越函数计算例程中使用它，在那里一切都是近似的。
 *
 *	尽管我们提供了一个 "round" 参数以与 div_var 一致，但不建议在 round=false 时使用此功能。在截断模式下，可能会得到没有有效位数字的结果，例如在 rscale=0 时，我们可能计算出 0.99999... 并将其截断为 0，而正确答案是 1。
 */
static void fc_div_var_fast(const NumericVar *fc_var1, const NumericVar *fc_var2,
			 NumericVar *fc_result, int fc_rscale, bool fc_round)
{
	int			fc_div_ndigits;
	int			fc_load_ndigits;
	int			fc_res_sign;
	int			fc_res_weight;
	int		   *fc_div;
	int			fc_qdigit;
	int			fc_carry;
	int			fc_maxdiv;
	int			fc_newdig;
	NumericDigit *fc_res_digits;
	double		fc_fdividend,
				fc_fdivisor,
				fc_fdivisorinverse,
				fc_fquotient;
	int			fc_qi;
	int			fc_i;

	/* 将这些值复制到局部变量中，以提高内部循环的速度 */
	int			fc_var1ndigits = fc_var1->ndigits;
	int			fc_var2ndigits = fc_var2->ndigits;
	NumericDigit *fc_var1digits = fc_var1->digits;
	NumericDigit *fc_var2digits = fc_var2->digits;

	/*
	 * 首先进行零除法检查；我们不能交给一个未规范化的除数。
	 */
	if (fc_var2ndigits == 0 || fc_var2digits[0] == 0)
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));

	/*
	 * 如果除数只有一位或两位数字，则委托给div_var_int()，它使用快速短除法。
	 */
	if (fc_var2ndigits <= 2)
	{
		int			fc_idivisor;
		int			fc_idivisor_weight;

		fc_idivisor = fc_var2->digits[0];
		fc_idivisor_weight = fc_var2->weight;
		if (fc_var2ndigits == 2)
		{
			fc_idivisor = fc_idivisor * NBASE + fc_var2->digits[1];
			fc_idivisor_weight--;
		}
		if (fc_var2->sign == NUMERIC_NEG)
			fc_idivisor = -fc_idivisor;

		fc_div_var_int(fc_var1, fc_idivisor, fc_idivisor_weight, fc_result, fc_rscale, fc_round);
		return;
	}

	/*
	 * 否则，执行完整的长除法。
	 */

	/* 检查结果是否为零 */
	if (fc_var1ndigits == 0)
	{
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/*
	 * 确定结果的符号、权重和计算的数字位数
	 */
	if (fc_var1->sign == fc_var2->sign)
		fc_res_sign = NUMERIC_POS;
	else
		fc_res_sign = NUMERIC_NEG;
	fc_res_weight = fc_var1->weight - fc_var2->weight + 1;
	/* 我们需要产生的准确结果数字位数： */
	fc_div_ndigits = fc_res_weight + 1 + (fc_rscale + DEC_DIGITS - 1) / DEC_DIGITS;
	/* 添加保护数字以防止舍入误差 */
	fc_div_ndigits += DIV_GUARD_DIGITS;
	if (fc_div_ndigits < DIV_GUARD_DIGITS)
		fc_div_ndigits = DIV_GUARD_DIGITS;

	/*
	 * 我们在一个名为 "div[]" 的有符号整数数组中进行算术运算。由于 INT_MAX 显著大于 NBASE*NBASE，因此我们有足够的空间来避免立即标准化进位。
	 *
	 * 我们从 div[] 开始，里面包含一个零位，后面是被除数的数字（加上附加的零以达到所需的精度，包括保护数字）。主循环的每一步计算一个（近似）商位并将其存储到 div[]，移除一个被除数空间的位置。最后一次进位传播处理任何错误的商位。
	 *
	 * 注意，div[] 不必包含被除数中的所有数字——所需的精度加上保护数字可能少于被除数的精度。这种情况通常发生在平方根算法中，我们通常将一个 2N 位数字除以一个 N 位数字，只需要一个 N 位精度的结果。
	 */
	fc_div = (int *) palloc0((fc_div_ndigits + 1) * sizeof(int));
	fc_load_ndigits = Min(fc_div_ndigits, fc_var1ndigits);
	for (fc_i = 0; fc_i < fc_load_ndigits; fc_i++)
		fc_div[fc_i + 1] = fc_var1digits[fc_i];

	/*
	 * 我们使用浮点算术来估计每个商位，取 (当前) 被除数和除数的前四位。这必须是浮点以避免溢出。商位一般不会超过精确答案的一个位数。
	 */
	fc_fdivisor = (double) fc_var2digits[0];
	for (fc_i = 1; fc_i < 4; fc_i++)
	{
		fc_fdivisor *= NBASE;
		if (fc_i < fc_var2ndigits)
			fc_fdivisor += (double) fc_var2digits[fc_i];
	}
	fc_fdivisorinverse = 1.0 / fc_fdivisor;

	/*
	 * maxdiv 跟踪任何 div[] 项的最大可能绝对值；当这威胁超过 INT_MAX 时，我们花时间传播进位。此外，我们需要确保在进位传播过程中不会发生溢出。进位值的绝对值可能高达 INT_MAX/NBASE + 1，因此实际上，当数字威胁超过 INT_MAX - INT_MAX/NBASE - 1 时，我们必须标准化。
	 *
	 * 为了避免 maxdiv 本身的溢出，它表示最大绝对值除以 NBASE-1，即在循环的顶部已知没有 div[] 项的绝对值超过 maxdiv * (NBASE-1)。
	 *
	 * 但是，实际上，这仅适用于 div[qi] 之后的 div[] 项；循环底部所做的调整可能导致 div[qi + 1] 超过 maxdiv 限制，因此在下一次迭代中 div[qi] 超出了限制。这不会造成问题，如下所述。
	 */
	fc_maxdiv = 1;

	/*
	 * 外部循环计算下一个商位，将进入 div[qi]
	 */
	for (fc_qi = 0; fc_qi < fc_div_ndigits; fc_qi++)
	{
		/* 近似当前被除数值 */
		fc_fdividend = (double) fc_div[fc_qi];
		for (fc_i = 1; fc_i < 4; fc_i++)
		{
			fc_fdividend *= NBASE;
			if (fc_qi + fc_i <= fc_div_ndigits)
				fc_fdividend += (double) fc_div[fc_qi + fc_i];
		}
		/* 计算（近似）商位 */
		fc_fquotient = fc_fdividend * fc_fdivisorinverse;
		fc_qdigit = (fc_fquotient >= 0.0) ? ((int) fc_fquotient) :
			(((int) fc_fquotient) - 1);	/* 向负无穷大截断 */

		if (fc_qdigit != 0)
		{
			/* 我们现在需要标准化吗？ */
			fc_maxdiv += Abs(fc_qdigit);
			if (fc_maxdiv > (INT_MAX - INT_MAX / NBASE - 1) / (NBASE - 1))
			{
				/*
				 * 是的，执行此操作。注意，如果 var2ndigits 远小于 div_ndigits，我们可以通过注意到只需对那些 div[] 项进行标准化来节省大量工作，这些项在先前的迭代中减去了除数的倍数。
				 */
				fc_carry = 0;
				for (fc_i = Min(fc_qi + fc_var2ndigits - 2, fc_div_ndigits); fc_i > fc_qi; fc_i--)
				{
					fc_newdig = fc_div[fc_i] + fc_carry;
					if (fc_newdig < 0)
					{
						fc_carry = -((-fc_newdig - 1) / NBASE) - 1;
						fc_newdig -= fc_carry * NBASE;
					}
					else if (fc_newdig >= NBASE)
					{
						fc_carry = fc_newdig / NBASE;
						fc_newdig -= fc_carry * NBASE;
					}
					else
						fc_carry = 0;
					fc_div[fc_i] = fc_newdig;
				}
				fc_newdig = fc_div[fc_qi] + fc_carry;
				fc_div[fc_qi] = fc_newdig;

				/*
				 * 除了可能的 div[qi] 之外，所有 div[] 位数现在都在 0..NBASE-1 的范围内。我们不再需要在 maxdiv 值中考虑 div[qi]，因此可以将 maxdiv 重置为 1。
				 */
				fc_maxdiv = 1;

				/*
				 * 重新计算商位，因为新信息可能已经传播到前四位被除数位数中
				 */
				fc_fdividend = (double) fc_div[fc_qi];
				for (fc_i = 1; fc_i < 4; fc_i++)
				{
					fc_fdividend *= NBASE;
					if (fc_qi + fc_i <= fc_div_ndigits)
						fc_fdividend += (double) fc_div[fc_qi + fc_i];
				}
				/* 计算（近似）商位 */
				fc_fquotient = fc_fdividend * fc_fdivisorinverse;
				fc_qdigit = (fc_fquotient >= 0.0) ? ((int) fc_fquotient) :
					(((int) fc_fquotient) - 1);	/* 向负无穷大截断 */
				fc_maxdiv += Abs(fc_qdigit);
			}

			
/*
			 * 减去适当倍数的除数。
			 *
			 * div[qi] 之后的数字不可能溢出，因为我们知道它们
			 * 将在 maxdiv 限制内。至于 div[qi] 本身，请注意
			 * qdigit 大约为 trunc(div[qi] / vardigits[0]),
			 * 这将使新值简单地为 div[qi] mod vardigits[0]。
			 * qdigit 中的低位项可以改变这个结果，最多
			 * 不超过约两倍的 INT_MAX/NBASE，因此不可能溢出。
			 *
			 * 这个内部循环是除法的性能瓶颈，因此
			 * 以与 mul_var() 的内部循环相同的方式编写代码，以便
			 * 允许自动向量化。我们在乘法之前将 qdigit 强制转换为 NumericDigit
			 * 以允许编译器生成更高效的代码（使用 16 位乘法），这是安全的
			 * 因为我们知道商数字最多偏差一个，因此
			 * 不存在溢出风险。
			 */
			if (fc_qdigit != 0)
			{
				int			fc_istop = Min(fc_var2ndigits, fc_div_ndigits - fc_qi + 1);
				int		   *fc_div_qi = &fc_div[fc_qi];

				for (fc_i = 0; fc_i < fc_istop; fc_i++)
					fc_div_qi[fc_i] -= ((NumericDigit) fc_qdigit) * fc_var2digits[fc_i];
			}
		}

		/*
		 * 我们即将替换的被除数数字可能仍然不为零。
		 * 将其折叠到下一个数字位置。
		 *
		 * 在这里没有溢出的风险，尽管证明这一点需要
		 * 一些小心。就像证明 div[qi] 不会溢出一样，
		 * 如果我们考虑 qdigit 的分子和分母中的前两个项，
		 * 我们可以看到 div[qi + 1] 的最终值将
		 * 大约是一个模 (vardigits[0]*NBASE + vardigits[1]) 的余数。
		 * 由于低位项的考虑有点复杂，但最终
		 * 增加的范围不超过 INT_MAX/NBASE。
		 * 因此，div[qi + 1] 在这里不会溢出，同时作为 div[qi]
		 * 在下一个循环迭代中，它也不可能大到导致
		 * 进位传播步骤（如果有的话）中溢出。
		 *
		 * 但是这么说：div[qi] 可以大于 INT_MAX/NBASE，正如
		 * 上面所指出的，这意味着乘积 div[qi] * NBASE *可能*
		 * 会溢出。当这种情况发生时，将其添加到 div[qi + 1] 
		 * 将始终导致一个抵消溢出，从而使最终结果是正确的。我们
		 * 可以通过在 int64 算法中进行乘法和加法来避免中间溢出，
		 * 但到目前为止似乎没有必要。
		 */
		fc_div[fc_qi + 1] += fc_div[fc_qi] * NBASE;

		fc_div[fc_qi] = fc_qdigit;
	}

	/*
	 * 近似并存储最后的商数字 (div[div_ndigits])
	 */
	fc_fdividend = (double) fc_div[fc_qi];
	for (fc_i = 1; fc_i < 4; fc_i++)
		fc_fdividend *= NBASE;
	fc_fquotient = fc_fdividend * fc_fdivisorinverse;
	fc_qdigit = (fc_fquotient >= 0.0) ? ((int) fc_fquotient) :
		(((int) fc_fquotient) - 1);	/* 向负无穷大截断 */
	fc_div[fc_qi] = fc_qdigit;

	/*
	 * 因为商数字可能有一个偏差，所以此时其中一些数字可能是
	 * -1 或 NBASE。代表值在数学上是正确的，但外观不正确。
	 * 我们进行最后的进位传播以规范化数字，并将其与将结果
	 * 数字存储到输出中结合。请注意，这仍然在具有保留数字的
	 * 全精度下完成。
	 */
	fc_alloc_var(fc_result, fc_div_ndigits + 1);
	fc_res_digits = fc_result->digits;
	fc_carry = 0;
	for (fc_i = fc_div_ndigits; fc_i >= 0; fc_i--)
	{
		fc_newdig = fc_div[fc_i] + fc_carry;
		if (fc_newdig < 0)
		{
			fc_carry = -((-fc_newdig - 1) / NBASE) - 1;
			fc_newdig -= fc_carry * NBASE;
		}
		else if (fc_newdig >= NBASE)
		{
			fc_carry = fc_newdig / NBASE;
			fc_newdig -= fc_carry * NBASE;
		}
		else
			fc_carry = 0;
		fc_res_digits[fc_i] = fc_newdig;
	}
	Assert(fc_carry == 0);

	pfree(fc_div);

	/*
	 * 最后，将结果四舍五入到请求的精度。
	 */
	fc_result->weight = fc_res_weight;
	fc_result->sign = fc_res_sign;

	/* 四舍五入到目标rscale（并设置结果->dscale） */
	if (fc_round)
		fc_round_var(fc_result, fc_rscale);
	else
		fc_trunc_var(fc_result, fc_rscale);

	/* 去掉前导和尾随零 */
	fc_strip_var(fc_result);
}


/*
 * div_var_int() -
 *
 *	用指定权重将数字变量除以 32 位整数。
 *	商 var / (ival * NBASE^ival_weight) 存储在结果中。
 */
static void fc_div_var_int(const NumericVar *fc_var, int fc_ival, int fc_ival_weight,
			NumericVar *fc_result, int fc_rscale, bool fc_round)
{
	NumericDigit *fc_var_digits = fc_var->digits;
	int			fc_var_ndigits = fc_var->ndigits;
	int			fc_res_sign;
	int			fc_res_weight;
	int			fc_res_ndigits;
	NumericDigit *fc_res_buf;
	NumericDigit *fc_res_digits;
	uint32		fc_divisor;
	int			fc_i;

	/* 防止除以零 */
	if (fc_ival == 0)
		ereport(ERROR,
				errcode(ERRCODE_DIVISION_BY_ZERO),
				errmsg("division by zero"));

	/* 检查结果是否为零 */
	if (fc_var_ndigits == 0)
	{
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	
/*
	 * 确定结果的符号、权重和需要计算的数字位数。
	 * 这里计算的权重是正确的，如果发出的商没有
	 * 前导零数字；否则 strip_var() 将会修正。
	 */
	if (fc_var->sign == NUMERIC_POS)
		fc_res_sign = fc_ival > 0 ? NUMERIC_POS : NUMERIC_NEG;
	else
		fc_res_sign = fc_ival > 0 ? NUMERIC_NEG : NUMERIC_POS;
	fc_res_weight = fc_var->weight - fc_ival_weight;
	/* 我们需要产生的准确结果数字位数： */
	fc_res_ndigits = fc_res_weight + 1 + (fc_rscale + DEC_DIGITS - 1) / DEC_DIGITS;
	/* ... 但至少要 1 */
	fc_res_ndigits = Max(fc_res_ndigits, 1);
	/* 如果需要四舍五入，那么计算多出一位以确保结果正确 */
	if (fc_round)
		fc_res_ndigits++;

	fc_res_buf = digitbuf_alloc(fc_res_ndigits + 1);
	fc_res_buf[0] = 0;				/* 为后续舍入保留的数字 */
	fc_res_digits = fc_res_buf + 1;

	/*
	 * 现在计算商数字。这是 Knuth 第 2 卷第 4.3.1 节习题 16 中描述的
	 * 短除法算法，除了我们允许除数超过内部基数。
	 *
	 * 在这个算法中，从一个数字到下一个数字的进位至多是
	 * divisor - 1。因此，在处理下一个数字时，进位可能变得
	 * 大到 divisor * NBASE - 1，如果这超过了 UINT_MAX， 
	 * 那么它需要一个 64 位整数。
	 */
	fc_divisor = Abs(fc_ival);

	if (fc_divisor <= UINT_MAX / NBASE)
	{
		/* 进位不能溢出 32 位 */
		uint32		fc_carry = 0;

		for (fc_i = 0; fc_i < fc_res_ndigits; fc_i++)
		{
			fc_carry = fc_carry * NBASE + (fc_i < fc_var_ndigits ? fc_var_digits[fc_i] : 0);
			fc_res_digits[fc_i] = (NumericDigit) (fc_carry / fc_divisor);
			fc_carry = fc_carry % fc_divisor;
		}
	}
	else
	{
		/* 进位可能超过 32 位 */
		uint64		fc_carry = 0;

		for (fc_i = 0; fc_i < fc_res_ndigits; fc_i++)
		{
			fc_carry = fc_carry * NBASE + (fc_i < fc_var_ndigits ? fc_var_digits[fc_i] : 0);
			fc_res_digits[fc_i] = (NumericDigit) (fc_carry / fc_divisor);
			fc_carry = fc_carry % fc_divisor;
		}
	}

	/* 将商存储在结果中 */
	digitbuf_free(fc_result->buf);
	fc_result->ndigits = fc_res_ndigits;
	fc_result->buf = fc_res_buf;
	fc_result->digits = fc_res_digits;
	fc_result->weight = fc_res_weight;
	fc_result->sign = fc_res_sign;

	/* 四舍五入或截断到目标 rscale（并设置 result->dscale） */
	if (fc_round)
		fc_round_var(fc_result, fc_rscale);
	else
		fc_trunc_var(fc_result, fc_rscale);

	/* 移除前导/尾随零 */
	fc_strip_var(fc_result);
}


/*
 * 默认的除法尺度选择
 *
 * 返回适合除法结果的结果尺度。
 */
static int fc_select_div_scale(const NumericVar *fc_var1, const NumericVar *fc_var2)
{
	int			fc_weight1,
				fc_weight2,
				fc_qweight,
				fc_i;
	NumericDigit fc_firstdigit1,
				fc_firstdigit2;
	int			fc_rscale;

	/*
	 * 除法的结果尺度在任何 SQL 标准中都没有规定。对于
	 * PostgreSQL，我们选择一个结果尺度，以保证至少
	 * NUMERIC_MIN_SIG_DIGITS 个有效数字，从而保证 numeric 
	 * 的结果不低于 float8 的精度；但使用的尺度不小于
	 * 任一输入的显示尺度。
	 */

	/* 获取每个输入的实际（标准化）权重和首位数字 */

	fc_weight1 = 0;				/* 当 var1 为零时要使用的值 */
	fc_firstdigit1 = 0;
	for (fc_i = 0; fc_i < fc_var1->ndigits; fc_i++)
	{
		fc_firstdigit1 = fc_var1->digits[fc_i];
		if (fc_firstdigit1 != 0)
		{
			fc_weight1 = fc_var1->weight - fc_i;
			break;
		}
	}

	fc_weight2 = 0;				/* 当 var2 为零时要使用的值 */
	fc_firstdigit2 = 0;
	for (fc_i = 0; fc_i < fc_var2->ndigits; fc_i++)
	{
		fc_firstdigit2 = fc_var2->digits[fc_i];
		if (fc_firstdigit2 != 0)
		{
			fc_weight2 = fc_var2->weight - fc_i;
			break;
		}
	}

	/*
	 * 估算商的权重。如果两个首位数字相等，我们
	 * 不能确定，但假设 var1 小于 var2。
	 */
	fc_qweight = fc_weight1 - fc_weight2;
	if (fc_firstdigit1 <= fc_firstdigit2)
		fc_qweight--;

	/* 选择结果缩放 */
	fc_rscale = NUMERIC_MIN_SIG_DIGITS - fc_qweight * DEC_DIGITS;
	fc_rscale = Max(fc_rscale, fc_var1->dscale);
	fc_rscale = Max(fc_rscale, fc_var2->dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	return fc_rscale;
}


/*
 * mod_var() -
 *
 *	在变量级别计算两个数字的模
 */
static void fc_mod_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	NumericVar	fc_tmp;

	init_var(&fc_tmp);

	/* ---------
	 * 我们使用以下等式
	 *		mod(x,y) = x - trunc(x/y)*y
	 * div_var 可以直接给我们 trunc(x/y) 的结果。
	 * ----------
	 */
	fc_div_var(fc_var1, fc_var2, &fc_tmp, 0, false);

	fc_mul_var(fc_var2, &fc_tmp, &fc_tmp, fc_var2->dscale);

	fc_sub_var(fc_var1, &fc_tmp, fc_result);

	fc_free_var(&fc_tmp);
}


/*
 * div_mod_var() -
 *
 *	计算两个数字变量的截断整数商和数字余数。
 * 余数的精度为 var2 的 dscale。
 */
static void fc_div_mod_var(const NumericVar *fc_var1, const NumericVar *fc_var2,
			NumericVar *fc_quot, NumericVar *fc_rem)
{
	NumericVar	fc_q;
	NumericVar	fc_r;

	init_var(&fc_q);
	init_var(&fc_r);

	/*
	 * 使用 div_var_fast() 获得整数商的初步估计。
	 * 这可能不准确（根据 div_var_fast 的注释中的警告），
	 * 但我们可以在下面进行更正。
	 */
	fc_div_var_fast(fc_var1, fc_var2, &fc_q, 0, false);

	/* 使用商估计计算初始余数估计。 */
	fc_mul_var(fc_var2, &fc_q, &fc_r, fc_var2->dscale);
	fc_sub_var(fc_var1, &fc_r, &fc_r);

	/*
	 * 如果必要，调整结果 --- 余数应该与 var1 有相同的
	 * 符号，并且其绝对值应该小于 var2 的绝对值。
	 */
	while (fc_r.ndigits != 0 && fc_r.sign != fc_var1->sign)
	{
		/* 商的绝对值太大 */
		if (fc_var1->sign == fc_var2->sign)
		{
			fc_sub_var(&fc_q, &const_one, &fc_q);
			add_var(&fc_r, fc_var2, &fc_r);
		}
		else
		{
			add_var(&fc_q, &const_one, &fc_q);
			fc_sub_var(&fc_r, fc_var2, &fc_r);
		}
	}

	while (fc_cmp_abs(&fc_r, fc_var2) >= 0)
	{
		/* 商的绝对值太小 */
		if (fc_var1->sign == fc_var2->sign)
		{
			add_var(&fc_q, &const_one, &fc_q);
			fc_sub_var(&fc_r, fc_var2, &fc_r);
		}
		else
		{
			fc_sub_var(&fc_q, &const_one, &fc_q);
			add_var(&fc_r, fc_var2, &fc_r);
		}
	}

	set_var_from_var(&fc_q, fc_quot);
	set_var_from_var(&fc_r, fc_rem);

	fc_free_var(&fc_q);
	fc_free_var(&fc_r);
}


/*
 * ceil_var() -
 *
 *	返回大于或等于参数的最小整数
 * 在变量级别
 */
static void fc_ceil_var(const NumericVar *fc_var, NumericVar *fc_result)
{
	NumericVar	fc_tmp;

	init_var(&fc_tmp);
	set_var_from_var(fc_var, &fc_tmp);

	fc_trunc_var(&fc_tmp, 0);

	if (fc_var->sign == NUMERIC_POS && fc_cmp_var(fc_var, &fc_tmp) != 0)
		add_var(&fc_tmp, &const_one, &fc_tmp);

	set_var_from_var(&fc_tmp, fc_result);
	fc_free_var(&fc_tmp);
}


/*
 * floor_var() -
 *
 *	返回小于或等于参数的最大整数
 * 在变量级别
 */
static void fc_floor_var(const NumericVar *fc_var, NumericVar *fc_result)
{
	NumericVar	fc_tmp;

	init_var(&fc_tmp);
	set_var_from_var(fc_var, &fc_tmp);

	fc_trunc_var(&fc_tmp, 0);

	if (fc_var->sign == NUMERIC_NEG && fc_cmp_var(fc_var, &fc_tmp) != 0)
		fc_sub_var(&fc_tmp, &const_one, &fc_tmp);

	set_var_from_var(&fc_tmp, fc_result);
	fc_free_var(&fc_tmp);
}


/*
 * gcd_var() -
 *
 *	计算两个数字的最大公约数在变量级别
 */
static void fc_gcd_var(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	int			fc_res_dscale;
	int			fc_cmp;
	NumericVar	fc_tmp_arg;
	NumericVar	fc_mod;

	fc_res_dscale = Max(fc_var1->dscale, fc_var2->dscale);

	/*
	 * 确保 var1 是绝对值较大的数字。
	 *
	 * 这将在下面的循环中自动发生，但可以避免
	 * 昂贵的模运算。
	 */
	fc_cmp = fc_cmp_abs(fc_var1, fc_var2);
	if (fc_cmp < 0)
	{
		const NumericVar *fc_tmp = fc_var1;

		fc_var1 = fc_var2;
		fc_var2 = fc_tmp;
	}

	/*
	 * 如果输入的绝对值相同，或者较小的输入为零，
	 * 还应避免取模。
	 */
	if (fc_cmp == 0 || fc_var2->ndigits == 0)
	{
		set_var_from_var(fc_var1, fc_result);
		fc_result->sign = NUMERIC_POS;
		fc_result->dscale = fc_res_dscale;
		return;
	}

	init_var(&fc_tmp_arg);
	init_var(&fc_mod);

	/* 使用欧几里得算法找到 GCD */
	set_var_from_var(fc_var1, &fc_tmp_arg);
	set_var_from_var(fc_var2, fc_result);

	for (;;)
	{
		/* 这个循环可能会耗时，因此允许其被中断 */
		CHECK_FOR_INTERRUPTS();

		fc_mod_var(&fc_tmp_arg, fc_result, &fc_mod);
		if (fc_mod.ndigits == 0)
			break;
		set_var_from_var(fc_result, &fc_tmp_arg);
		set_var_from_var(&fc_mod, fc_result);
	}
	fc_result->sign = NUMERIC_POS;
	fc_result->dscale = fc_res_dscale;

	fc_free_var(&fc_tmp_arg);
	fc_free_var(&fc_mod);
}



/*
 * sqrt_var() -
 *
 *	使用 Karatsuba 平方根算法计算 x 的平方根。
 *	注意：我们允许 rscale < 0，这意味着在小数点之前四舍五入。
 */
static void fc_sqrt_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale)
{
	int			fc_stat;
	int			fc_res_weight;
	int			fc_res_ndigits;
	int			fc_src_ndigits;
	int			fc_step;
	int			fc_ndigits[32];
	int			fc_blen;
	int64		fc_arg_int64;
	int			fc_src_idx;
	int64		fc_s_int64;
	int64		fc_r_int64;
	NumericVar	fc_s_var;
	NumericVar	fc_r_var;
	NumericVar	fc_a0_var;
	NumericVar	fc_a1_var;
	NumericVar	fc_q_var;
	NumericVar	fc_u_var;

	fc_stat = fc_cmp_var(fc_arg, &const_zero);
	if (fc_stat == 0)
	{
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/*
	 * SQL2003 将 sqrt() 定义为幂运算，因此如果操作数为负数，
	 * 我们需要发出正确的 SQLSTATE 错误代码。
	 */
	if (fc_stat < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
				 errmsg("cannot take square root of a negative number")));

	init_var(&fc_s_var);
	init_var(&fc_r_var);
	init_var(&fc_a0_var);
	init_var(&fc_a1_var);
	init_var(&fc_q_var);
	init_var(&fc_u_var);

	/*
	 * 结果权重是输入权重的一半，向负无穷舍入 --- res_weight = floor(arg->weight / 2)。
	 */
	if (fc_arg->weight >= 0)
		fc_res_weight = fc_arg->weight / 2;
	else
		fc_res_weight = -((-fc_arg->weight - 1) / 2 + 1);

	/*
	 * 要计算的NBASE数字数量。为了确保正确的舍入，至少计算
	 * 1个额外的小数位。我们在这里明确允许rscale为负，但必须始终计算至少1个NBASE数字。
	 * 因此res_ndigits = res_weight + 1 + ceil((rscale + 1) / DEC_DIGITS) 或 1。
	 */
	if (fc_rscale + 1 >= 0)
		fc_res_ndigits = fc_res_weight + 1 + (fc_rscale + DEC_DIGITS) / DEC_DIGITS;
	else
		fc_res_ndigits = fc_res_weight + 1 - (-fc_rscale - 1) / DEC_DIGITS;
	fc_res_ndigits = Max(fc_res_ndigits, 1);

	/*
	 * 产生具有此精度结果所需的源NBASE数字数量 --- 每个小数点前的数字，
	 * 加上每个小数点后结果数字的2（或减去每个小数点前舍入的结果数字的2）。
	 */
	fc_src_ndigits = fc_arg->weight + 1 + (fc_res_ndigits - fc_res_weight - 1) * 2;
	fc_src_ndigits = Max(fc_src_ndigits, 1);

	/* ----------
	 * 从这一点开始，我们将输入和结果视为整数，并使用Karatsuba
	 * 平方根算法计算整数平方根和余数，该算法可以递归地写成如下形式：
	 *
	 *	SqrtRem(n = a3*b^3 + a2*b^2 + a1*b + a0):
	 *		[ 对于某个基数b，以及选择的系数a0,a1,a2,a3，使得
	 *		  0 <= a0,a1,a2 < b 且 a3 >= b/4 ]
	 *		设(s,r) = SqrtRem(a3*b + a2)
	 *		设(q,u) = DivRem(r*b + a1, 2*s)
	 *		设s = s*b + q
	 *		设r = u*b + a0 - q^2
	 *		如果r < 0则
	 *			设r = r + s
	 *			设s = s - 1
	 *			设r = r + s
	 *		返回(s,r)
	 *
	 * 参见 "Karatsuba Square Root"，Paul Zimmermann，INRIA研究报告
	 * RR-3805，1999年11月。  写作时，该论文在网络上可通过
	 * <https://hal.inria.fr/inria-00072854>获取。
	 *
	 * 假设“n = a3*b^3 + a2*b^2 + a1*b + a0”可以这样理解：
	 * “选择一个基数b，使得n至少需要四个基数b数字才能
	 * 表达；然后这些数字是a3,a2,a1,a0，且a3可能大于
	 * b”。为了获得最佳性能，b应该大约有输入数字的四分之一，
	 * 以便外部平方根计算的数字数量大约是内部的两倍。
	 * 为了简便起见，我们选择b = NBASE^blen，NBASE的整数幂。
	 *
	 * 我们以迭代方式实现该算法，而不是递归，以
	 * 允许重用工作变量。通过这种方法，输入的每个数字
	 * 被精确读取一次 --- src_idx跟踪到目前为止使用的输入数字数量。
	 *
	 * 数组ndigits[]存储在每次迭代结束时将使用的
	 * 输入的NBASE数字数量，该数量大约每次翻倍。
	 * 注意数组元素以反向顺序存储，因此如果最终迭代需要
	 * src_ndigits = 37个输入数字，该数组将包含[37,19,11,7,5,3]，
	 * 我们将首先计算3个最重要的NBASE数字的平方根。
	 *
	 * 在每次迭代中，我们选择blen为最大的整数，使得当
	 * 输入数字以上述形式书写时，a3 >= b/4。一般而言，
	 * 这意味着blen = src_ndigits / 4（截断），但如果
	 * src_ndigits是4的倍数，则可能导致系数a3小于
	 * b/4（如果第一个输入数字小于NBASE/4），在这种情况下
	 * 我们选择blen = src_ndigits / 4 - 1。内层平方根中的数字
	 * 数量则为src_ndigits - 2*blen。因此，例如，如果我们
	 * 最初有src_ndigits = 26，则数组ndigits[]将是
	 * [26,14,8,4]或[26,14,8,6,4]，具体取决于第一个
	 * 输入数字的大小。
	 *
	 * 此外，我们可以给所需步骤的数量设定上限 --- 假设
	 * 源数字的数量是一个位数为n的数字，范围在[2^(n-1)，2^n-1]内，
	 * 那么blen将位于范围[2^(n-3)-1，2^(n-2)-1]内，
	 * 内部平方根中的数字数量将位于范围 [2^(n-2)，2^(n-1)+1]内。
	 * 在下一步中，blen将位于范围[2^(n-4)-1，2^(n-3)]内，
	 * 下一次内层平方根中的数字数量将位于范围
	 * [2^(n-3)，2^(n-2)+1]内。这个模式会重复，在最坏
	 * 的情况下，数组ndigits[]将包含[2^n-1，2^(n-1)+1，2^(n-2)+1，
	 * ... 9，5，3]，计算将需要n步。因此，由于所有数字
	 * 数组大小都是有符号32位整数，所需的步骤数量保证
	 * 小于32。
	 * ----------
	 */
	fc_step = 0;
	while ((fc_ndigits[fc_step] = fc_src_ndigits) > 4)
	{
		/* 选择b，使得a3 >= b/4，如上所述 */
		fc_blen = fc_src_ndigits / 4;
		if (fc_blen * 4 == fc_src_ndigits && fc_arg->digits[0] < NBASE / 4)
			fc_blen--;

		/* 下一步中的数字数量（内层平方根） */
		fc_src_ndigits -= 2 * fc_blen;
		fc_step++;
	}

	/*
	 * 第一次迭代（最内层平方根和余数）：
	 *
	 * 此时src_ndigits <= 4，输入适合int64。它的平方根
	 * 至多有9位小数，因此使用双精度
	 * 运算进行估算，这实际上几乎肯定会返回正确的
	 * 结果，无需进一步修正。
	 */
	fc_arg_int64 = fc_arg->digits[0];
	for (fc_src_idx = 1; fc_src_idx < fc_src_ndigits; fc_src_idx++)
	{
		fc_arg_int64 *= NBASE;
		if (fc_src_idx < fc_arg->ndigits)
			fc_arg_int64 += fc_arg->digits[fc_src_idx];
	}

	fc_s_int64 = (int64) sqrt((double) fc_arg_int64);
	fc_r_int64 = fc_arg_int64 - fc_s_int64 * fc_s_int64;

	
/*
	 * 如果有必要，使用牛顿法来修正结果。
	 *
	 * 这通过使用截断的整数除法来计算截断的整数平方根，通过使用公式 x -> (x + n/x) 进行迭代。
	 * 这被认为会收敛到 isqrt(n)，除非 n+1 是完全平方数。
	 * 如果 n+1 是完全平方数，则序列将在两个值 isqrt(n) 和 isqrt(n)+1 之间振荡，因此我们可以通过
	 * 检查余数来确保收敛。
	 */
	while (fc_r_int64 < 0 || fc_r_int64 > 2 * fc_s_int64)
	{
		fc_s_int64 = (fc_s_int64 + fc_arg_int64 / fc_s_int64) / 2;
		fc_r_int64 = fc_arg_int64 - fc_s_int64 * fc_s_int64;
	}

	/*
	 * 测试次数小于等于 8 的 src_ndigits :
	 *
	 * 接下来的 1 或 2 次迭代计算更大的 (外部) 平方根，且 src_ndigits <= 8，因此结果仍然适合 int64（即使输入不再适合），
	 * 我们可以继续使用 int64 变量计算，以避免更昂贵的数值计算。
	 *
	 * 观察到中间值溢出 64 位整数的风险相对较小。在最坏的情况下，前一次迭代将计算出一个 3 位数平方根（小于 NBASE^6 / 4 的 6 位数输入），
	 * 因此在此迭代开始时，s 将小于 NBASE^3 / 2 = 10^12 / 2，且 r 将小于 10^12。 在此情况下，blen 将为 1，因此 numer 将小于 10^17，
	 * 而 denom 将小于 10^12（因此 u 也会小于 10^12）。最后，由于 q^2 = u*b + a0 - r，我们也可以确信 q^2 < 10^17。
	 * 因此所有这些数量都可以舒适地放入 64 位整数中。
	 */
	fc_step--;
	while (fc_step >= 0 && (fc_src_ndigits = fc_ndigits[fc_step]) <= 8)
	{
		int			fc_b;
		int			fc_a0;
		int			fc_a1;
		int			fc_i;
		int64		fc_numer;
		int64		fc_denom;
		int64		fc_q;
		int64		fc_u;

		fc_blen = (fc_src_ndigits - fc_src_idx) / 2;

		/* 提取 a1 和 a0，并计算 b */
		fc_a0 = 0;
		fc_a1 = 0;
		fc_b = 1;

		for (fc_i = 0; fc_i < fc_blen; fc_i++, fc_src_idx++)
		{
			fc_b *= NBASE;
			fc_a1 *= NBASE;
			if (fc_src_idx < fc_arg->ndigits)
				fc_a1 += fc_arg->digits[fc_src_idx];
		}

		for (fc_i = 0; fc_i < fc_blen; fc_i++, fc_src_idx++)
		{
			fc_a0 *= NBASE;
			if (fc_src_idx < fc_arg->ndigits)
				fc_a0 += fc_arg->digits[fc_src_idx];
		}

		/* 计算 (q,u) = DivRem(r*b + a1, 2*s) */
		fc_numer = fc_r_int64 * fc_b + fc_a1;
		fc_denom = 2 * fc_s_int64;
		fc_q = fc_numer / fc_denom;
		fc_u = fc_numer - fc_q * fc_denom;

		/* 计算 s = s*b + q 和 r = u*b + a0 - q^2 */
		fc_s_int64 = fc_s_int64 * fc_b + fc_q;
		fc_r_int64 = fc_u * fc_b + fc_a0 - fc_q * fc_q;

		if (fc_r_int64 < 0)
		{
			/* s 大了一；设 r += s, s--, r += s */
			fc_r_int64 += fc_s_int64;
			fc_s_int64--;
			fc_r_int64 += fc_s_int64;
		}

		Assert(fc_src_idx == fc_src_ndigits); /* 所有输入数字已用尽 */
		fc_step--;
	}

	/*
	 * 在支持 128 位整数的平台上，我们可以进一步推迟使用数值变量的需求。
	 */
#ifdef HAVE_INT128
	if (fc_step >= 0)
	{
		int128		fc_s_int128;
		int128		fc_r_int128;

		fc_s_int128 = fc_s_int64;
		fc_r_int128 = fc_r_int64;

		/*
		 * 测试次数小于等于 16 的 src_ndigits :
		 *
		 * 结果适合 int128（即使输入不适合），因此我们使用 int128 变量以避免更昂贵的数值计算。
		 */
		while (fc_step >= 0 && (fc_src_ndigits = fc_ndigits[fc_step]) <= 16)
		{
			int64		fc_b;
			int64		fc_a0;
			int64		fc_a1;
			int64		fc_i;
			int128		fc_numer;
			int128		fc_denom;
			int128		fc_q;
			int128		fc_u;

			fc_blen = (fc_src_ndigits - fc_src_idx) / 2;

			/* 提取 a1 和 a0，并计算 b */
			fc_a0 = 0;
			fc_a1 = 0;
			fc_b = 1;

			for (fc_i = 0; fc_i < fc_blen; fc_i++, fc_src_idx++)
			{
				fc_b *= NBASE;
				fc_a1 *= NBASE;
				if (fc_src_idx < fc_arg->ndigits)
					fc_a1 += fc_arg->digits[fc_src_idx];
			}

			for (fc_i = 0; fc_i < fc_blen; fc_i++, fc_src_idx++)
			{
				fc_a0 *= NBASE;
				if (fc_src_idx < fc_arg->ndigits)
					fc_a0 += fc_arg->digits[fc_src_idx];
			}

			/* 计算 (q,u) = DivRem(r*b + a1, 2*s) */
			fc_numer = fc_r_int128 * fc_b + fc_a1;
			fc_denom = 2 * fc_s_int128;
			fc_q = fc_numer / fc_denom;
			fc_u = fc_numer - fc_q * fc_denom;

			/* 计算 s = s*b + q 和 r = u*b + a0 - q^2 */
			fc_s_int128 = fc_s_int128 * fc_b + fc_q;
			fc_r_int128 = fc_u * fc_b + fc_a0 - fc_q * fc_q;

			if (fc_r_int128 < 0)
			{
				/* s 大了一；设 r += s, s--, r += s */
				fc_r_int128 += fc_s_int128;
				fc_s_int128--;
				fc_r_int128 += fc_s_int128;
			}

			Assert(fc_src_idx == fc_src_ndigits); /* 所有输入数字已用尽 */
			fc_step--;
		}

		/*
		 * 所有剩余的迭代需要数值变量。 将整数值转换为 NumericVar 并继续。 请注意，在最后一次迭代中，我们不需要余数，
		 * 因此我们可以在这里节省几个周期而不完全计算它。
		 */
		fc_int128_to_numericvar(fc_s_int128, &fc_s_var);
		if (fc_step >= 0)
			fc_int128_to_numericvar(fc_r_int128, &fc_r_var);
	}
	else
	{
		fc_int64_to_numericvar(fc_s_int64, &fc_s_var);
		/* 步骤 < 0，因此我们确实不需要 r */
	}
#else							/* !HAVE_INT128 */
	fc_int64_to_numericvar(fc_s_int64, &fc_s_var);
	if (fc_step >= 0)
		fc_int64_to_numericvar(fc_r_int64, &fc_r_var);
#endif							/* HAVE_INT128 */

	/*
	 * 剩余的迭代中 src_ndigits > 8（如果有 int128，则为 16）使用数值变量。
	 */
	while (fc_step >= 0)
	{
		int			fc_tmp_len;

		fc_src_ndigits = fc_ndigits[fc_step];
		fc_blen = (fc_src_ndigits - fc_src_idx) / 2;

		/* 提取 a1 和 a0 */
		if (fc_src_idx < fc_arg->ndigits)
		{
			fc_tmp_len = Min(fc_blen, fc_arg->ndigits - fc_src_idx);
			fc_alloc_var(&fc_a1_var, fc_tmp_len);
			memcpy(fc_a1_var.digits, fc_arg->digits + fc_src_idx,
				   fc_tmp_len * sizeof(NumericDigit));
			fc_a1_var.weight = fc_blen - 1;
			fc_a1_var.sign = NUMERIC_POS;
			fc_a1_var.dscale = 0;
			fc_strip_var(&fc_a1_var);
		}
		else
		{
			fc_zero_var(&fc_a1_var);
			fc_a1_var.dscale = 0;
		}
		fc_src_idx += fc_blen;

		if (fc_src_idx < fc_arg->ndigits)
		{
			fc_tmp_len = Min(fc_blen, fc_arg->ndigits - fc_src_idx);
			fc_alloc_var(&fc_a0_var, fc_tmp_len);
			memcpy(fc_a0_var.digits, fc_arg->digits + fc_src_idx,
				   fc_tmp_len * sizeof(NumericDigit));
			fc_a0_var.weight = fc_blen - 1;
			fc_a0_var.sign = NUMERIC_POS;
			fc_a0_var.dscale = 0;
			fc_strip_var(&fc_a0_var);
		}
		else
		{
			fc_zero_var(&fc_a0_var);
			fc_a0_var.dscale = 0;
		}
		fc_src_idx += fc_blen;

		/* 计算 (q,u) = DivRem(r*b + a1, 2*s) */
		set_var_from_var(&fc_r_var, &fc_q_var);
		fc_q_var.weight += fc_blen;
		add_var(&fc_q_var, &fc_a1_var, &fc_q_var);
		add_var(&fc_s_var, &fc_s_var, &fc_u_var);
		fc_div_mod_var(&fc_q_var, &fc_u_var, &fc_q_var, &fc_u_var);

		/* 计算 s = s*b + q */
		fc_s_var.weight += fc_blen;
		add_var(&fc_s_var, &fc_q_var, &fc_s_var);

		/*
		 * 计算 r = u*b + a0 - q^2。
		 *
		 * 在最后一次迭代中，我们实际上不需要 r；我们只需要知道它是否为负，以便我们知道是否调整 s。
		 * 因此，最后的减法可以改为比较。
		 */
		fc_u_var.weight += fc_blen;
		add_var(&fc_u_var, &fc_a0_var, &fc_u_var);
		fc_mul_var(&fc_q_var, &fc_q_var, &fc_q_var, 0);

		if (fc_step > 0)
		{
			/* 后续迭代需要 r */
			fc_sub_var(&fc_u_var, &fc_q_var, &fc_r_var);
			if (fc_r_var.sign == NUMERIC_NEG)
			{
				/* s 大了一；设 r += s, s--, r += s */
				add_var(&fc_r_var, &fc_s_var, &fc_r_var);
				fc_sub_var(&fc_s_var, &const_one, &fc_s_var);
				add_var(&fc_r_var, &fc_s_var, &fc_r_var);
			}
		}
		else
		{
			/* 不再需要 r，除非测试 s 是否大了一 */
			if (fc_cmp_var(&fc_u_var, &fc_q_var) < 0)
				fc_sub_var(&fc_s_var, &const_one, &fc_s_var);
		}

		Assert(fc_src_idx == fc_src_ndigits); /* 所有输入数字已用尽 */
		fc_step--;
	}

	/*
	 * 构造最终结果，将其四舍五入到请求的精度。
	 */
	set_var_from_var(&fc_s_var, fc_result);
	fc_result->weight = fc_res_weight;
	fc_result->sign = NUMERIC_POS;

	/* 四舍五入到目标rscale（并设置结果->dscale） */
	fc_round_var(fc_result, fc_rscale);

	/* 去掉前导和尾随零 */
	fc_strip_var(fc_result);

	fc_free_var(&fc_s_var);
	fc_free_var(&fc_r_var);
	fc_free_var(&fc_a0_var);
	fc_free_var(&fc_a1_var);
	fc_free_var(&fc_q_var);
	fc_free_var(&fc_u_var);
}


/*
 * exp_var() -
 *
 *	将 e 提升到 x 的幂，计算到 rscale 小数位
 */
static void fc_exp_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale)
{
	NumericVar	fc_x;
	NumericVar	fc_elem;
	int			fc_ni;
	double		fc_val;
	int			fc_dweight;
	int			fc_ndiv2;
	int			fc_sig_digits;
	int			fc_local_rscale;

	init_var(&fc_x);
	init_var(&fc_elem);

	set_var_from_var(fc_arg, &fc_x);

	/*
	 * 使用浮点算术估算结果的 dweight，以便我们可以选择一个合适的本地 rscale 进行计算。
	 */
	fc_val = fc_numericvar_to_double_no_overflow(&fc_x);

	/* 防止溢出/下溢 */
	/* 如果更改此限制，请查看 power_var() 的限制 */
	if (Abs(fc_val) >= NUMERIC_MAX_RESULT_SCALE * 3)
	{
		if (fc_val > 0)
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value overflows numeric format")));
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/* 十进制权重 = log10(e^x) = x * log10(e) */
	fc_dweight = (int) (fc_val * 0.434294481903252);

	/*
	 * 通过除以 2^ndiv2 将 x 降至范围 -0.01 <= x <= 0.01（近似），以提高泰勒级数的收敛速度。
	 *
	 * 请注意，上面的溢出检查确保 Abs(x) < 6000，这意味着这里 ndiv2 <= 20。
	 */
	if (Abs(fc_val) > 0.01)
	{
		fc_ndiv2 = 1;
		fc_val /= 2;

		while (Abs(fc_val) > 0.01)
		{
			fc_ndiv2++;
			fc_val /= 2;
		}

		fc_local_rscale = fc_x.dscale + fc_ndiv2;
		fc_div_var_int(&fc_x, 1 << fc_ndiv2, 0, &fc_x, fc_local_rscale, true);
	}
	else
		fc_ndiv2 = 0;

	/*
	 * 为泰勒级数展开设置比例因子。最终结果具有 (dweight + rscale + 1) 位有效数字。此外，我们必须将泰勒级数结果提升到 2^ndiv2 的幂，这引入了大约 log10(2^ndiv2) 位数字的误差，因此需要使用这么多额外的精度数字（加上几个额外的以确保准确）。
	 */
	fc_sig_digits = 1 + fc_dweight + fc_rscale + (int) (fc_ndiv2 * 0.301029995663981);
	fc_sig_digits = Max(fc_sig_digits, 0) + 8;

	fc_local_rscale = fc_sig_digits - 1;

	/*
	 * 使用泰勒级数
	 *
	 * exp(x) = 1 + x + x^2/2! + x^3/3! + ...
	 *
	 * 考虑到 x 的有限范围，这应该会迅速收敛。我们运行级数，直到各项低于 local_rscale 限制。
	 */
	add_var(&const_one, &fc_x, fc_result);

	fc_mul_var(&fc_x, &fc_x, &fc_elem, fc_local_rscale);
	fc_ni = 2;
	fc_div_var_int(&fc_elem, fc_ni, 0, &fc_elem, fc_local_rscale, true);

	while (fc_elem.ndigits != 0)
	{
		add_var(fc_result, &fc_elem, fc_result);

		fc_mul_var(&fc_elem, &fc_x, &fc_elem, fc_local_rscale);
		fc_ni++;
		fc_div_var_int(&fc_elem, fc_ni, 0, &fc_elem, fc_local_rscale, true);
	}

	/*
	 * 补偿参数范围的缩减。由于结果的权重随着每次乘法而翻倍，因此我们可以在进行过程中减少 local rscale。
	 */
	while (fc_ndiv2-- > 0)
	{
		fc_local_rscale = fc_sig_digits - fc_result->weight * 2 * DEC_DIGITS;
		fc_local_rscale = Max(fc_local_rscale, NUMERIC_MIN_DISPLAY_SCALE);
		fc_mul_var(fc_result, fc_result, fc_result, fc_local_rscale);
	}

	/* 四舍五入到请求的 rscale */
	fc_round_var(fc_result, fc_rscale);

	fc_free_var(&fc_x);
	fc_free_var(&fc_elem);
}


/*
 * 估计一个数字自然对数的最有效数字的 dweight。
 *
 * 实际上，我们在近似 log10(abs(ln(var)))。这用于计算自然对数时确定适当的 rscale。
 *
 * 注意：许多调用者在范围检查输入之前就调用了这个。因此，我们必须对无效应用 ln() 的值具有鲁棒性。我们不希望在这里抛出错误，因此在这种情况下只需返回零。
 */
static int fc_estimate_ln_dweight(const NumericVar *fc_var)
{
	int			fc_ln_dweight;

	/* 调用者应在 ln(负数) 时失败，但目前返回零 */
	if (fc_var->sign != NUMERIC_POS)
		return 0;

	if (fc_cmp_var(fc_var, &const_zero_point_nine) >= 0 &&
		fc_cmp_var(fc_var, &const_one_point_one) <= 0)
	{
		/*
		 * 0.9 <= var <= 1.1
		 *
		 * ln(var) 的权重为负值（可能非常大）。为了获得合理准确的结果，使用 ln(1+x) ~= x 进行估算。
		 */
		NumericVar	fc_x;

		init_var(&fc_x);
		fc_sub_var(fc_var, &const_one, &fc_x);

		if (fc_x.ndigits > 0)
		{
			/* 使用 x 最有效数字的权重 */
			fc_ln_dweight = fc_x.weight * DEC_DIGITS + (int) log10(fc_x.digits[0]);
		}
		else
		{
			/* x = 0。由于 ln(1) = 0，完全准确，因此不需要额外的数字 */
			fc_ln_dweight = 0;
		}

		fc_free_var(&fc_x);
	}
	else
	{
		/*
		 * 使用输入数字的前几个数字估算对数。每当输入不太接近 1 时，这将给出准确的结果。
		 */
		if (fc_var->ndigits > 0)
		{
			int			fc_digits;
			int			fc_dweight;
			double		fc_ln_var;

			fc_digits = fc_var->digits[0];
			fc_dweight = fc_var->weight * DEC_DIGITS;

			if (fc_var->ndigits > 1)
			{
				fc_digits = fc_digits * NBASE + fc_var->digits[1];
				fc_dweight -= DEC_DIGITS;
			}

			/*----------
			 * 我们有 var ~= digits * 10^dweight
			 * 所以 ln(var) ~= ln(digits) + dweight * ln(10)
			 *----------
			 */
			fc_ln_var = log((double) fc_digits) + fc_dweight * 2.302585092994046;
			fc_ln_dweight = (int) log10(Abs(fc_ln_var));
		}
		else
		{
			/* 调用者应在 ln(0) 时失败，但目前返回零 */
			fc_ln_dweight = 0;
		}
	}

	return fc_ln_dweight;
}


/*
 * ln_var() -
 *
 *	计算 x 的自然对数
 */
static void fc_ln_var(const NumericVar *fc_arg, NumericVar *fc_result, int fc_rscale)
{
	NumericVar	fc_x;
	NumericVar	fc_xx;
	int			fc_ni;
	NumericVar	fc_elem;
	NumericVar	fc_fact;
	int			fc_nsqrt;
	int			fc_local_rscale;
	int			fc_cmp;

	fc_cmp = fc_cmp_var(fc_arg, &const_zero);
	if (fc_cmp == 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of zero")));
	else if (fc_cmp < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of a negative number")));

	init_var(&fc_x);
	init_var(&fc_xx);
	init_var(&fc_elem);
	init_var(&fc_fact);

	set_var_from_var(fc_arg, &fc_x);
	set_var_from_var(&const_two, &fc_fact);

	/*
	 * 通过重复 sqrt() 操作将输入减少到范围 0.9 < x < 1.1。
	 *
	 * 最后的对数将具有大约 rscale+6 位有效数字。每次 sqrt() 将大约减半 x 的权重，因此在工作过程中调整 local rscale，以便在每一步保持这样多的有效数字（加上几个额外的以确保准确）。
	 *
	 * 请注意，在这个输入缩减过程中，我们允许 local_rscale < 0，这意味着在小数点之前进行舍入。sqrt_var() 明确支持这一点，并显著减少将非常大的输入减少到所需范围的工作量。一旦输入缩减完成，x.weight 将为 0，其显示比例将再次为非负值。
	 */
	fc_nsqrt = 0;
	while (fc_cmp_var(&fc_x, &const_zero_point_nine) <= 0)
	{
		fc_local_rscale = fc_rscale - fc_x.weight * DEC_DIGITS / 2 + 8;
		fc_sqrt_var(&fc_x, &fc_x, fc_local_rscale);
		fc_mul_var(&fc_fact, &const_two, &fc_fact, 0);
		fc_nsqrt++;
	}
	while (fc_cmp_var(&fc_x, &const_one_point_one) >= 0)
	{
		fc_local_rscale = fc_rscale - fc_x.weight * DEC_DIGITS / 2 + 8;
		fc_sqrt_var(&fc_x, &fc_x, fc_local_rscale);
		fc_mul_var(&fc_fact, &const_two, &fc_fact, 0);
		fc_nsqrt++;
	}

	/*
	 * 我们使用 0.5 * ln((1+z)/(1-z)) 的泰勒级数，
	 *
	 * z + z^3/3 + z^5/5 + ...
	 *
	 * 其中 z = (x-1)/(x+1)，由于 x 的上述范围缩减，近似在范围 (-0.053 .. 0.048) 内。
	 *
	 * 这个收敛性并没有想象中那么快，但鉴于 z 较小，是可以接受的。
	 *
	 * 泰勒级数结果将乘以 2^(nsqrt+1)，其十进制权重为 (nsqrt+1) * log10(2)，因此使用这么多额外的精度数字（加上几个额外的以确保准确）。
	 */
	fc_local_rscale = fc_rscale + (int) ((fc_nsqrt + 1) * 0.301029995663981) + 8;

	fc_sub_var(&fc_x, &const_one, fc_result);
	add_var(&fc_x, &const_one, &fc_elem);
	fc_div_var_fast(fc_result, &fc_elem, fc_result, fc_local_rscale, true);
	set_var_from_var(fc_result, &fc_xx);
	fc_mul_var(fc_result, fc_result, &fc_x, fc_local_rscale);

	fc_ni = 1;

	for (;;)
	{
		fc_ni += 2;
		fc_mul_var(&fc_xx, &fc_x, &fc_xx, fc_local_rscale);
		fc_div_var_int(&fc_xx, fc_ni, 0, &fc_elem, fc_local_rscale, true);

		if (fc_elem.ndigits == 0)
			break;

		add_var(fc_result, &fc_elem, fc_result);

		if (fc_elem.weight < (fc_result->weight - fc_local_rscale * 2 / DEC_DIGITS))
			break;
	}

	/* 补偿参数范围缩减，四舍五入到请求的 rscale */
	fc_mul_var(fc_result, &fc_fact, fc_result, fc_rscale);

	fc_free_var(&fc_x);
	fc_free_var(&fc_xx);
	fc_free_var(&fc_elem);
	fc_free_var(&fc_fact);
}


/*
 * log_var() -
 *
 *	计算 num 的某个底数的对数。
 *
 *	注意：此例程选择结果的 dscale。
 */
static void fc_log_var(const NumericVar *fc_base, const NumericVar *fc_num, NumericVar *fc_result)
{
	NumericVar	fc_ln_base;
	NumericVar	fc_ln_num;
	int			fc_ln_base_dweight;
	int			fc_ln_num_dweight;
	int			fc_result_dweight;
	int			fc_rscale;
	int			fc_ln_base_rscale;
	int			fc_ln_num_rscale;

	init_var(&fc_ln_base);
	init_var(&fc_ln_num);

	/* 估计 ln(base)、ln(num) 和最终结果的 dweights */
	fc_ln_base_dweight = fc_estimate_ln_dweight(fc_base);
	fc_ln_num_dweight = fc_estimate_ln_dweight(fc_num);
	fc_result_dweight = fc_ln_num_dweight - fc_ln_base_dweight;

	/*
	 * 选择结果的比例，使其至少具有 NUMERIC_MIN_SIG_DIGITS 位有效数字，并且不小于任何输入的显示比例。
	 */
	fc_rscale = NUMERIC_MIN_SIG_DIGITS - fc_result_dweight;
	fc_rscale = Max(fc_rscale, fc_base->dscale);
	fc_rscale = Max(fc_rscale, fc_num->dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	/*
	 * 为 ln(base) 和 ln(num) 设置比例，使它们每个都比最终结果有更多的有效数字。
	 */
	fc_ln_base_rscale = fc_rscale + fc_result_dweight - fc_ln_base_dweight + 8;
	fc_ln_base_rscale = Max(fc_ln_base_rscale, NUMERIC_MIN_DISPLAY_SCALE);

	fc_ln_num_rscale = fc_rscale + fc_result_dweight - fc_ln_num_dweight + 8;
	fc_ln_num_rscale = Max(fc_ln_num_rscale, NUMERIC_MIN_DISPLAY_SCALE);

	/* 形成自然对数 */
	fc_ln_var(fc_base, &fc_ln_base, fc_ln_base_rscale);
	fc_ln_var(fc_num, &fc_ln_num, fc_ln_num_rscale);

	/* 按要求的比例进行除法和四舍五入 */
	fc_div_var_fast(&fc_ln_num, &fc_ln_base, fc_result, fc_rscale, true);

	fc_free_var(&fc_ln_num);
	fc_free_var(&fc_ln_base);
}


/*
 * power_var() -
 *
 *	将底数提升到 exp 的幂
 *
 *	注意：此例程选择结果的 dscale。
 */
static void fc_power_var(const NumericVar *fc_base, const NumericVar *fc_exp, NumericVar *fc_result)
{
	int			fc_res_sign;
	NumericVar	fc_abs_base;
	NumericVar	fc_ln_base;
	NumericVar	fc_ln_num;
	int			fc_ln_dweight;
	int			fc_rscale;
	int			fc_sig_digits;
	int			fc_local_rscale;
	double		fc_val;

	/* 如果 exp 可以表示为整数，则使用 power_var_int */
	if (fc_exp->ndigits == 0 || fc_exp->ndigits <= fc_exp->weight + 1)
	{
		/* 精确整数，但它适合 int 吗？ */
		int64		fc_expval64;

		if (fc_numericvar_to_int64(fc_exp, &fc_expval64))
		{
			if (fc_expval64 >= PG_INT32_MIN && fc_expval64 <= PG_INT32_MAX)
			{
				/* 好吧，选择 rscale */
				fc_rscale = NUMERIC_MIN_SIG_DIGITS;
				fc_rscale = Max(fc_rscale, fc_base->dscale);
				fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
				fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

				fc_power_var_int(fc_base, (int) fc_expval64, fc_result, fc_rscale);
				return;
			}
		}
	}

	/*
	 * 这避免了 0 的非整数次幂的 log(0) 情况。0 ^ 0
	 * 由 power_var_int() 处理。
	 */
	if (fc_cmp_var(fc_base, &const_zero) == 0)
	{
		set_var_from_var(&const_zero, fc_result);
		fc_result->dscale = NUMERIC_MIN_SIG_DIGITS;	/* 无需四舍五入 */
		return;
	}

	init_var(&fc_abs_base);
	init_var(&fc_ln_base);
	init_var(&fc_ln_num);

	/*
	 * 如果底数为负，则必须要求指数为整数。结果在指数为偶数时为正，在指数为奇数时为负。
	 */
	if (fc_base->sign == NUMERIC_NEG)
	{
		/*
		 * 检查指数是否为整数。此错误代码由 SQL 标准定义，并与 numeric_power() 中的其他错误匹配。
		 */
		if (fc_exp->ndigits > 0 && fc_exp->ndigits > fc_exp->weight + 1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
					 errmsg("a negative number raised to a non-integer power yields a complex result")));

		/* 测试指数是奇数还是偶数 */
		if (fc_exp->ndigits > 0 && fc_exp->ndigits == fc_exp->weight + 1 &&
			(fc_exp->digits[fc_exp->ndigits - 1] & 1))
			fc_res_sign = NUMERIC_NEG;
		else
			fc_res_sign = NUMERIC_POS;

		/* 然后在下面处理 abs(base) */
		set_var_from_var(fc_base, &fc_abs_base);
		fc_abs_base.sign = NUMERIC_POS;
		fc_base = &fc_abs_base;
	}
	else
		fc_res_sign = NUMERIC_POS;

	/*----------
	 * 决定 ln() 计算的规模。为此，我们需要结果的权重估计，通过初步低精度计算 exp * ln(base) 来获得。
	 *
	 * 我们希望结果 = e ^ (exp * ln(base))
	 * 因此结果的 dweight = log10(结果) = exp * ln(base) * log10(e)
	 *
	 * 我们还在这里执行粗略的溢出测试，以便在全精度结果肯定会溢出时提前退出，并防止在确定真实计算的规模时遇到整数溢出。
	 * exp_var() 支持输入高达 NUMERIC_MAX_RESULT_SCALE * 3，因此如果 exp * ln(base) >= NUMERIC_MAX_RESULT_SCALE * 3，结果将溢出。
	 * 由于这些值只是近似值，我们在这个溢出测试中应用一个小的模糊因子，让 exp_var() 确定确切的溢出阈值，以便所有输入的一致性。
	 *----------
	 */
	fc_ln_dweight = fc_estimate_ln_dweight(fc_base);

	/*
	 * 为低精度计算设置规模，计算 ln(base) 到大约 8 位有效数字。注意，ln_dweight 可能小到 -NUMERIC_DSCALE_MAX，因此这里的规模可能超过 NUMERIC_MAX_DISPLAY_SCALE。
	 */
	fc_local_rscale = 8 - fc_ln_dweight;
	fc_local_rscale = Max(fc_local_rscale, NUMERIC_MIN_DISPLAY_SCALE);

	fc_ln_var(fc_base, &fc_ln_base, fc_local_rscale);

	fc_mul_var(&fc_ln_base, fc_exp, &fc_ln_num, fc_local_rscale);

	fc_val = fc_numericvar_to_double_no_overflow(&fc_ln_num);

	/* 初始溢出/下溢测试与模糊因子 */
	if (Abs(fc_val) > NUMERIC_MAX_RESULT_SCALE * 3.01)
	{
		if (fc_val > 0)
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("value overflows numeric format")));
		fc_zero_var(fc_result);
		fc_result->dscale = NUMERIC_MAX_DISPLAY_SCALE;
		return;
	}

	fc_val *= 0.434294481903252;	/* 近似十进制结果权重 */

	/* 选择结果规模 */
	fc_rscale = NUMERIC_MIN_SIG_DIGITS - (int) fc_val;
	fc_rscale = Max(fc_rscale, fc_base->dscale);
	fc_rscale = Max(fc_rscale, fc_exp->dscale);
	fc_rscale = Max(fc_rscale, NUMERIC_MIN_DISPLAY_SCALE);
	fc_rscale = Min(fc_rscale, NUMERIC_MAX_DISPLAY_SCALE);

	/* 结果中所需的有效数字 */
	fc_sig_digits = fc_rscale + (int) fc_val;
	fc_sig_digits = Max(fc_sig_digits, 0);

	/* 为真实的 exp * ln(base) 计算设置规模 */
	fc_local_rscale = fc_sig_digits - fc_ln_dweight + 8;
	fc_local_rscale = Max(fc_local_rscale, NUMERIC_MIN_DISPLAY_SCALE);

	/* 并进行真实计算 */

	fc_ln_var(fc_base, &fc_ln_base, fc_local_rscale);

	fc_mul_var(&fc_ln_base, fc_exp, &fc_ln_num, fc_local_rscale);

	fc_exp_var(&fc_ln_num, fc_result, fc_rscale);

	if (fc_res_sign == NUMERIC_NEG && fc_result->ndigits > 0)
		fc_result->sign = NUMERIC_NEG;

	fc_free_var(&fc_ln_num);
	fc_free_var(&fc_ln_base);
	fc_free_var(&fc_abs_base);
}

/*
 * power_var_int() -
 *
 *	将底数提升到指数 exp 的幂，其中 exp 是整数。
 */
static void fc_power_var_int(const NumericVar *fc_base, int fc_exp, NumericVar *fc_result, int fc_rscale)
{
	double		fc_f;
	int			fc_p;
	int			fc_i;
	int			fc_sig_digits;
	unsigned int fc_mask;
	bool		fc_neg;
	NumericVar	fc_base_prod;
	int			fc_local_rscale;

	/* 处理一些常见的特殊情况，以及边缘情况 */
	switch (fc_exp)
	{
		case 0:

			/*
			 * 虽然 0 ^ 0 可以是 1 或不确定（错误），但我们将其视为 1，因为大多数编程语言这样做。 SQL:2003
			 * 也要求返回值为 1。
			 * https://en.wikipedia.org/wiki/Exponentiation#Zero_to_the_zero_power
			 */
			set_var_from_var(&const_one, fc_result);
			fc_result->dscale = fc_rscale;	/* 无需四舍五入 */
			return;
		case 1:
			set_var_from_var(fc_base, fc_result);
			fc_round_var(fc_result, fc_rscale);
			return;
		case -1:
			fc_div_var(&const_one, fc_base, fc_result, fc_rscale, true);
			return;
		case 2:
			fc_mul_var(fc_base, fc_base, fc_result, fc_rscale);
			return;
		default:
			break;
	}

	/* 处理底数为零的特殊情况 */
	if (fc_base->ndigits == 0)
	{
		if (fc_exp < 0)
			ereport(ERROR,
					(errcode(ERRCODE_DIVISION_BY_ZERO),
					 errmsg("division by zero")));
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/*
	 * 一般情况根据指数的位模式反复乘以底数。
	 *
	 * 首先我们需要估计结果的权重，以便知道需要多少有效数字。
	 */
	fc_f = fc_base->digits[0];
	fc_p = fc_base->weight * DEC_DIGITS;

	for (fc_i = 1; fc_i < fc_base->ndigits && fc_i * DEC_DIGITS < 16; fc_i++)
	{
		fc_f = fc_f * NBASE + fc_base->digits[fc_i];
		fc_p -= DEC_DIGITS;
	}

	/*----------
	 * 我们有 base ~= f * 10^p
	 * 因此 log10(结果) = log10(base^exp) ~= exp * (log10(f) + p)
	 *----------
	 */
	fc_f = fc_exp * (log10(fc_f) + fc_p);

	/*
	 * 应用粗略的溢出/下溢测试，以便在结果肯定会溢出/下溢时提前退出。
	 */
	if (fc_f > 3 * NUMERIC_WEIGHT_MAX * DEC_DIGITS)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value overflows numeric format")));
	if (fc_f + 1 < -fc_rscale || fc_f + 1 < -NUMERIC_MAX_DISPLAY_SCALE)
	{
		fc_zero_var(fc_result);
		fc_result->dscale = fc_rscale;
		return;
	}

	/*
	 * 结果中有效数字的近似数量。注意，以上的下溢测试意味着这一定是 >= 0。
	 */
	fc_sig_digits = 1 + fc_rscale + (int) fc_f;

	/*
	 * 生成结果的乘法可能引入大约 log10(abs(exp)) 位数字的误差，因此需要使用这种精度的额外数字（加上几个用于保险）。
	 */
	fc_sig_digits += (int) log(fabs((double) fc_exp)) + 8;

	/*
	 * 现在我们可以进行乘法操作。
	 */
	fc_neg = (fc_exp < 0);
	fc_mask = Abs(fc_exp);

	init_var(&fc_base_prod);
	set_var_from_var(fc_base, &fc_base_prod);

	if (fc_mask & 1)
		set_var_from_var(fc_base, fc_result);
	else
		set_var_from_var(&const_one, fc_result);

	while ((fc_mask >>= 1) > 0)
	{
		/*
		 * 使用足够大的 rscales 进行乘法，以容纳所需有效数字的结果，但不要浪费时间超出数字本身的规模。
		 */
		fc_local_rscale = fc_sig_digits - 2 * fc_base_prod.weight * DEC_DIGITS;
		fc_local_rscale = Min(fc_local_rscale, 2 * fc_base_prod.dscale);
		fc_local_rscale = Max(fc_local_rscale, NUMERIC_MIN_DISPLAY_SCALE);

		fc_mul_var(&fc_base_prod, &fc_base_prod, &fc_base_prod, fc_local_rscale);

		if (fc_mask & 1)
		{
			fc_local_rscale = fc_sig_digits -
				(fc_base_prod.weight + fc_result->weight) * DEC_DIGITS;
			fc_local_rscale = Min(fc_local_rscale,
							   fc_base_prod.dscale + fc_result->dscale);
			fc_local_rscale = Max(fc_local_rscale, NUMERIC_MIN_DISPLAY_SCALE);

			fc_mul_var(&fc_base_prod, fc_result, fc_result, fc_local_rscale);
		}

		/*
		 * 当 abs(base) > 1 时，base_prod 中小数点左侧的数字在每次迭代中加倍，因此如果 exp 很大，我们可能会耗费大量时间和内存空间进行乘法操作。
		 * 但是，一旦权重超过能够适应 int16 的值，最终结果肯定会溢出（或下溢，如果 exp < 0），因此我们可以在浪费过多周期之前放弃。
		 */
		if (fc_base_prod.weight > NUMERIC_WEIGHT_MAX ||
			fc_result->weight > NUMERIC_WEIGHT_MAX)
		{
			/* 溢出，除非为负，在这种情况下结果应为 0 */
			if (!fc_neg)
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("value overflows numeric format")));
			fc_zero_var(fc_result);
			fc_neg = false;
			break;
		}
	}

	fc_free_var(&fc_base_prod);

	/* 考虑输入符号，并舍入到请求的 rscale */
	if (fc_neg)
		fc_div_var_fast(&const_one, fc_result, fc_result, fc_rscale, true);
	else
		fc_round_var(fc_result, fc_rscale);
}

/*
 * power_ten_int() -
 *
 *	将十提升到指数 exp 的幂，其中 exp 是整数。请注意，与 power_var_int() 不同，此方法不进行溢出/下溢检查或舍入。
 */
static void fc_power_ten_int(int fc_exp, NumericVar *fc_result)
{
	/* 直接构造结果，从 10^0 = 1 开始 */
	set_var_from_var(&const_one, fc_result);

	/* 表示结果所需的规模 */
	fc_result->dscale = fc_exp < 0 ? -fc_exp : 0;

	/* 结果和剩余指数的 Base-NBASE 权重 */
	if (fc_exp >= 0)
		fc_result->weight = fc_exp / DEC_DIGITS;
	else
		fc_result->weight = (fc_exp + 1) / DEC_DIGITS - 1;

	fc_exp -= fc_result->weight * DEC_DIGITS;

	/****** 最终调整结果的单个 NBASE 数字 *******/
	while (fc_exp-- > 0)
		fc_result->digits[0] *= 10;
}


/****** ----------------------------------------------------------------------
 *
 * 以下是操作无符号变量级别的最低级函数
 *
 * ----------------------------------------------------------------------
 ******/


/* ----------
 * cmp_abs() -
 *
 *	比较var1和var2的绝对值
 *	返回：	-1 表示 ABS(var1) < ABS(var2)
 *				0  表示 ABS(var1) == ABS(var2)
 *				1  表示 ABS(var1) > ABS(var2)
 * ----------
 */
static int fc_cmp_abs(const NumericVar *fc_var1, const NumericVar *fc_var2)
{
	return fc_cmp_abs_common(fc_var1->digits, fc_var1->ndigits, fc_var1->weight,
						  fc_var2->digits, fc_var2->ndigits, fc_var2->weight);
}

/****** ----------
 * cmp_abs_common() -
 *
 *	比较绝对值的主程序。该函数可以被 NumericVar 和 Numeric 两者使用。
 * ----------
 ******/
static int fc_cmp_abs_common(const NumericDigit *fc_var1digits, int fc_var1ndigits, int fc_var1weight,
			   const NumericDigit *fc_var2digits, int fc_var2ndigits, int fc_var2weight)
{
	int			fc_i1 = 0;
	int			fc_i2 = 0;

	/****** 检查第一个公共数字之前的任何数字 ******/

	while (fc_var1weight > fc_var2weight && fc_i1 < fc_var1ndigits)
	{
		if (fc_var1digits[fc_i1++] != 0)
			return 1;
		fc_var1weight--;
	}
	while (fc_var2weight > fc_var1weight && fc_i2 < fc_var2ndigits)
	{
		if (fc_var2digits[fc_i2++] != 0)
			return -1;
		fc_var2weight--;
	}

	/****** 此时，w1 == w2 或者我们已经没有更多数字 ******/

	if (fc_var1weight == fc_var2weight)
	{
		while (fc_i1 < fc_var1ndigits && fc_i2 < fc_var2ndigits)
		{
			int			fc_stat = fc_var1digits[fc_i1++] - fc_var2digits[fc_i2++];

			if (fc_stat)
			{
				if (fc_stat > 0)
					return 1;
				return -1;
			}
		}
	}

	/******
	 * 此时，我们已经在一侧或另一侧耗尽了数字；因此任何
	 * 剩余的非零数字意味着该侧更大
	 ******/
	while (fc_i1 < fc_var1ndigits)
	{
		if (fc_var1digits[fc_i1++] != 0)
			return 1;
	}
	while (fc_i2 < fc_var2ndigits)
	{
		if (fc_var2digits[fc_i2++] != 0)
			return -1;
	}

	return 0;
}


/******
 * add_abs() -
 *
 *	将两个变量的绝对值相加并存储在结果中。
 *	结果可能指向其中一个操作数而没有危险。
 ******/
static void add_abs(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	NumericDigit *fc_res_buf;
	NumericDigit *fc_res_digits;
	int			fc_res_ndigits;
	int			fc_res_weight;
	int			fc_res_rscale,
				fc_rscale1,
				fc_rscale2;
	int			fc_res_dscale;
	int			fc_i,
				fc_i1,
				fc_i2;
	int			fc_carry = 0;

	/* 将这些值复制到局部变量中，以提高内部循环的速度 */
	int			fc_var1ndigits = fc_var1->ndigits;
	int			fc_var2ndigits = fc_var2->ndigits;
	NumericDigit *fc_var1digits = fc_var1->digits;
	NumericDigit *fc_var2digits = fc_var2->digits;

	fc_res_weight = Max(fc_var1->weight, fc_var2->weight) + 1;

	fc_res_dscale = Max(fc_var1->dscale, fc_var2->dscale);

	/****** 注意：在这里我们计算 rscale 在 base-NBASE 数字中 ******/
	fc_rscale1 = fc_var1->ndigits - fc_var1->weight - 1;
	fc_rscale2 = fc_var2->ndigits - fc_var2->weight - 1;
	fc_res_rscale = Max(fc_rscale1, fc_rscale2);

	fc_res_ndigits = fc_res_rscale + fc_res_weight + 1;
	if (fc_res_ndigits <= 0)
		fc_res_ndigits = 1;

	fc_res_buf = digitbuf_alloc(fc_res_ndigits + 1);
	fc_res_buf[0] = 0;				/* 为后续舍入保留的数字 */
	fc_res_digits = fc_res_buf + 1;

	fc_i1 = fc_res_rscale + fc_var1->weight + 1;
	fc_i2 = fc_res_rscale + fc_var2->weight + 1;
	for (fc_i = fc_res_ndigits - 1; fc_i >= 0; fc_i--)
	{
		fc_i1--;
		fc_i2--;
		if (fc_i1 >= 0 && fc_i1 < fc_var1ndigits)
			fc_carry += fc_var1digits[fc_i1];
		if (fc_i2 >= 0 && fc_i2 < fc_var2ndigits)
			fc_carry += fc_var2digits[fc_i2];

		if (fc_carry >= NBASE)
		{
			fc_res_digits[fc_i] = fc_carry - NBASE;
			fc_carry = 1;
		}
		else
		{
			fc_res_digits[fc_i] = fc_carry;
			fc_carry = 0;
		}
	}

	Assert(fc_carry == 0);			/****** 否则我们未能考虑进位 ******/

	digitbuf_free(fc_result->buf);
	fc_result->ndigits = fc_res_ndigits;
	fc_result->buf = fc_res_buf;
	fc_result->digits = fc_res_digits;
	fc_result->weight = fc_res_weight;
	fc_result->dscale = fc_res_dscale;

	/****** 去掉前导/尾随零 ******/
	fc_strip_var(fc_result);
}


/******
 * sub_abs()
 *
 *	从 var1 的绝对值中减去 var2 的绝对值
 *	并存储在结果中。结果可能指向其中一个操作数
 *	而没有危险。
 *
 *	ABS(var1) 必须大于或等于 ABS(var2) !!!
 ******/
static void fc_sub_abs(const NumericVar *fc_var1, const NumericVar *fc_var2, NumericVar *fc_result)
{
	NumericDigit *fc_res_buf;
	NumericDigit *fc_res_digits;
	int			fc_res_ndigits;
	int			fc_res_weight;
	int			fc_res_rscale,
				fc_rscale1,
				fc_rscale2;
	int			fc_res_dscale;
	int			fc_i,
				fc_i1,
				fc_i2;
	int			fc_borrow = 0;

	/* 将这些值复制到局部变量中，以提高内部循环的速度 */
	int			fc_var1ndigits = fc_var1->ndigits;
	int			fc_var2ndigits = fc_var2->ndigits;
	NumericDigit *fc_var1digits = fc_var1->digits;
	NumericDigit *fc_var2digits = fc_var2->digits;

	fc_res_weight = fc_var1->weight;

	fc_res_dscale = Max(fc_var1->dscale, fc_var2->dscale);

	/****** 注意：在这里我们计算 rscale 在 base-NBASE 数字中 ******/
	fc_rscale1 = fc_var1->ndigits - fc_var1->weight - 1;
	fc_rscale2 = fc_var2->ndigits - fc_var2->weight - 1;
	fc_res_rscale = Max(fc_rscale1, fc_rscale2);

	fc_res_ndigits = fc_res_rscale + fc_res_weight + 1;
	if (fc_res_ndigits <= 0)
		fc_res_ndigits = 1;

	fc_res_buf = digitbuf_alloc(fc_res_ndigits + 1);
	fc_res_buf[0] = 0;				/* 为后续舍入保留的数字 */
	fc_res_digits = fc_res_buf + 1;

	fc_i1 = fc_res_rscale + fc_var1->weight + 1;
	fc_i2 = fc_res_rscale + fc_var2->weight + 1;
	for (fc_i = fc_res_ndigits - 1; fc_i >= 0; fc_i--)
	{
		fc_i1--;
		fc_i2--;
		if (fc_i1 >= 0 && fc_i1 < fc_var1ndigits)
			fc_borrow += fc_var1digits[fc_i1];
		if (fc_i2 >= 0 && fc_i2 < fc_var2ndigits)
			fc_borrow -= fc_var2digits[fc_i2];

		if (fc_borrow < 0)
		{
			fc_res_digits[fc_i] = fc_borrow + NBASE;
			fc_borrow = -1;
		}
		else
		{
			fc_res_digits[fc_i] = fc_borrow;
			fc_borrow = 0;
		}
	}

	Assert(fc_borrow == 0);		/****** 否则调用者给了我们 var1 < var2 ******/

	digitbuf_free(fc_result->buf);
	fc_result->ndigits = fc_res_ndigits;
	fc_result->buf = fc_res_buf;
	fc_result->digits = fc_res_digits;
	fc_result->weight = fc_res_weight;
	fc_result->dscale = fc_res_dscale;

	/****** 去掉前导/尾随零 ******/
	fc_strip_var(fc_result);
}

/******
 * round_var
 *
 * 将变量的值舍入到小数点后不超过 rscale 位数字。
 * 注意：我们允许此处 rscale < 0，意味着
 * 在小数点前进行舍入。
 ******/
static void fc_round_var(NumericVar *fc_var, int fc_rscale)
{
	NumericDigit *fc_digits = fc_var->digits;
	int			fc_di;
	int			fc_ndigits;
	int			fc_carry;

	fc_var->dscale = fc_rscale;

	/****** 想要的小数位数 ******/
	fc_di = (fc_var->weight + 1) * DEC_DIGITS + fc_rscale;

	/******
	 * 如果 di = 0，值失去所有数字，但如果其
	 * 第一个额外的数字 >= 5，则可以舍入到 1。 如果 di < 0 结果必须为 0。
	******/
	if (fc_di < 0)
	{
		fc_var->ndigits = 0;
		fc_var->weight = 0;
		fc_var->sign = NUMERIC_POS;
	}
	else
	{
		/****** 想要的 NBASE 位数 ******/
		fc_ndigits = (fc_di + DEC_DIGITS - 1) / DEC_DIGITS;

		/****** 0，或在最后一个 NBASE 数字中保留的小数位数 ******/
		fc_di %= DEC_DIGITS;

		if (fc_ndigits < fc_var->ndigits ||
			(fc_ndigits == fc_var->ndigits && fc_di > 0))
		{
			fc_var->ndigits = fc_ndigits;

#if DEC_DIGITS == 1
			/****** di 必须为零 ******/
			carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0;
#else
			if (fc_di == 0)
				fc_carry = (fc_digits[fc_ndigits] >= HALF_NBASE) ? 1 : 0;
			else
			{
				/****** 必须在最后一个 NBASE 数字内舍入 ******/
				int			fc_extra,
							fc_pow10;

#if DEC_DIGITS == 4
				fc_pow10 = round_powers[fc_di];
#elif DEC_DIGITS == 2
				pow10 = 10;
#else
#error unsupported NBASE
#endif
				fc_extra = fc_digits[--fc_ndigits] % fc_pow10;
				fc_digits[fc_ndigits] -= fc_extra;
				fc_carry = 0;
				if (fc_extra >= fc_pow10 / 2)
				{
					fc_pow10 += fc_digits[fc_ndigits];
					if (fc_pow10 >= NBASE)
					{
						fc_pow10 -= NBASE;
						fc_carry = 1;
					}
					fc_digits[fc_ndigits] = fc_pow10;
				}
			}
#endif

			/****** 如果需要则传播进位 ******/
			while (fc_carry)
			{
				fc_carry += fc_digits[--fc_ndigits];
				if (fc_carry >= NBASE)
				{
					fc_digits[fc_ndigits] = fc_carry - NBASE;
					fc_carry = 1;
				}
				else
				{
					fc_digits[fc_ndigits] = fc_carry;
					fc_carry = 0;
				}
			}

			if (fc_ndigits < 0)
			{
				Assert(fc_ndigits == -1);	/****** 最好不要添加 > 1 位数字 ******/
				Assert(fc_var->digits > fc_var->buf);
				fc_var->digits--;
				fc_var->ndigits++;
				fc_var->weight++;
			}
		}
	}
}

/******
 * trunc_var
 *
 * 在小数点后 rscale 位数字处截断（向零）变量的值。
 * 注意：我们允许此处 rscale < 0，意味着
 * 在小数点前截断。
 ******/
static void fc_trunc_var(NumericVar *fc_var, int fc_rscale)
{
	int			fc_di;
	int			fc_ndigits;

	fc_var->dscale = fc_rscale;

	/****** 想要的小数位数 ******/
	fc_di = (fc_var->weight + 1) * DEC_DIGITS + fc_rscale;

	/******
	 * 如果 di <= 0，值失去所有数字。
	******/
	if (fc_di <= 0)
	{
		fc_var->ndigits = 0;
		fc_var->weight = 0;
		fc_var->sign = NUMERIC_POS;
	}
	else
	{
		/****** 想要的 NBASE 位数 ******/
		fc_ndigits = (fc_di + DEC_DIGITS - 1) / DEC_DIGITS;

		if (fc_ndigits <= fc_var->ndigits)
		{
			fc_var->ndigits = fc_ndigits;

#if DEC_DIGITS == 1
			/****** 内部数字的内容不必担心 ******/
#else
			/****** 0，或在最后一个 NBASE 数字中保留的小数位数 ******/
			fc_di %= DEC_DIGITS;

			if (fc_di > 0)
			{
				/****** 必须在最后一个 NBASE 数字内截断 ******/
				NumericDigit *fc_digits = fc_var->digits;
				int			fc_extra,
							fc_pow10;

#if DEC_DIGITS == 4
				fc_pow10 = round_powers[fc_di];
#elif DEC_DIGITS == 2
				pow10 = 10;
#else
#error unsupported NBASE
#endif
				fc_extra = fc_digits[--fc_ndigits] % fc_pow10;
				fc_digits[fc_ndigits] -= fc_extra;
			}
#endif
		}
	}
}

/******
 * strip_var
 *
 * 从数值变量中去掉任何前导和尾随零
 ******/
static void fc_strip_var(NumericVar *fc_var)
{
	NumericDigit *fc_digits = fc_var->digits;
	int			fc_ndigits = fc_var->ndigits;

	/****** 去掉前导零 ******/
	while (fc_ndigits > 0 && *fc_digits == 0)
	{
		fc_digits++;
		fc_var->weight--;
		fc_ndigits--;
	}

	/****** 去掉尾随零 ******/
	while (fc_ndigits > 0 && fc_digits[fc_ndigits - 1] == 0)
		fc_ndigits--;

	/****** 如果它是零，则标准化符号和权重 ******/
	if (fc_ndigits == 0)
	{
		fc_var->sign = NUMERIC_POS;
		fc_var->weight = 0;
	}

	fc_var->digits = fc_digits;
	fc_var->ndigits = fc_ndigits;
}


/****** ----------------------------------------------------------------------
 *
 * 快速求和累加器函数
 *
 * ----------------------------------------------------------------------
 ******/ 

/*
 * 将累加器的值重置为零。保存数字的缓冲区不会被释放。
 */
static void fc_accum_sum_reset(NumericSumAccum *fc_accum)
{
	int			fc_i;

	fc_accum->dscale = 0;
	for (fc_i = 0; fc_i < fc_accum->ndigits; fc_i++)
	{
		fc_accum->pos_digits[fc_i] = 0;
		fc_accum->neg_digits[fc_i] = 0;
	}
}

/*
 * 累加一个新值。
 */
static void fc_accum_sum_add(NumericSumAccum *fc_accum, const NumericVar *fc_val)
{
	int32	   *fc_accum_digits;
	int			fc_i,
				fc_val_i;
	int			fc_val_ndigits;
	NumericDigit *fc_val_digits;

	/*
	 * 如果自上次进位传播以来我们累加了太多值，现在就进行进位传播，以避免溢出。
	 * （如果我们为进位传播保留两个额外的数字而不是一个，我们可以允许超过 NBASE - 1 的值。
	 * 但是即使是 NBASE - 1，这也需要非常少地进行，因此性能差异微乎其微。）
	 */
	if (fc_accum->num_uncarried == NBASE - 1)
		fc_accum_sum_carry(fc_accum);

	/*
	 * 调整旧值的权重或规模，以便能够容纳新值。
	 */
	fc_accum_sum_rescale(fc_accum, fc_val);

	/* */
	if (fc_val->sign == NUMERIC_POS)
		fc_accum_digits = fc_accum->pos_digits;
	else
		fc_accum_digits = fc_accum->neg_digits;

	/* 将这些值复制到局部变量中以提高循环速度 */
	fc_val_ndigits = fc_val->ndigits;
	fc_val_digits = fc_val->digits;

	fc_i = fc_accum->weight - fc_val->weight;
	for (fc_val_i = 0; fc_val_i < fc_val_ndigits; fc_val_i++)
	{
		fc_accum_digits[fc_i] += (int32) fc_val_digits[fc_val_i];
		fc_i++;
	}

	fc_accum->num_uncarried++;
}

/*
 * 传播进位。
 */
static void fc_accum_sum_carry(NumericSumAccum *fc_accum)
{
	int			fc_i;
	int			fc_ndigits;
	int32	   *fc_dig;
	int32		fc_carry;
	int32		fc_newdig = 0;

	/*
	 * 如果自上次进位传播以来没有添加新值，则无需做任何事情。
	 */
	if (fc_accum->num_uncarried == 0)
		return;

	/*
	 * 我们保持累加器的权重总是比当前值所需的权重大一，以确保在进位传播时有足够的空间
	 * 存放可能的额外数字。除非我们要求 accum_sum_final() 的调用者切换到正确的内存上下文，
	 * 否则我们不能在这里扩展缓冲区。
	 */
	Assert(fc_accum->pos_digits[0] == 0 && fc_accum->neg_digits[0] == 0);

	fc_ndigits = fc_accum->ndigits;

	/* 在正和中传播进位 */
	fc_dig = fc_accum->pos_digits;
	fc_carry = 0;
	for (fc_i = fc_ndigits - 1; fc_i >= 0; fc_i--)
	{
		fc_newdig = fc_dig[fc_i] + fc_carry;
		if (fc_newdig >= NBASE)
		{
			fc_carry = fc_newdig / NBASE;
			fc_newdig -= fc_carry * NBASE;
		}
		else
			fc_carry = 0;
		fc_dig[fc_i] = fc_newdig;
	}
	/* 我们是否用完了保留以进行进位传播的数字？ */
	if (fc_newdig > 0)
		fc_accum->have_carry_space = false;

	/* 对于负和也是如此 */
	fc_dig = fc_accum->neg_digits;
	fc_carry = 0;
	for (fc_i = fc_ndigits - 1; fc_i >= 0; fc_i--)
	{
		fc_newdig = fc_dig[fc_i] + fc_carry;
		if (fc_newdig >= NBASE)
		{
			fc_carry = fc_newdig / NBASE;
			fc_newdig -= fc_carry * NBASE;
		}
		else
			fc_carry = 0;
		fc_dig[fc_i] = fc_newdig;
	}
	if (fc_newdig > 0)
		fc_accum->have_carry_space = false;

	fc_accum->num_uncarried = 0;
}

/*
 * 重新调整累加器的规模以容纳新值。
 *
 * 如果新值的数字位数超过了累加器当前的数字缓冲区，则扩大缓冲区。
 */
static void fc_accum_sum_rescale(NumericSumAccum *fc_accum, const NumericVar *fc_val)
{
	int			fc_old_weight = fc_accum->weight;
	int			fc_old_ndigits = fc_accum->ndigits;
	int			fc_accum_ndigits;
	int			fc_accum_weight;
	int			fc_accum_rscale;
	int			fc_val_rscale;

	fc_accum_weight = fc_old_weight;
	fc_accum_ndigits = fc_old_ndigits;

	/*
	 * 新值的权重更大吗？如果是，扩大缓冲区，并通过添加前导零将现有值移至新权重。
	 *
	 * 我们强制累加器的权重总是比输入所需的权重大一，以便在最终的进位传播阶段
	 * 有空间存放额外的数字（如果需要）。
	 */
	if (fc_val->weight >= fc_accum_weight)
	{
		fc_accum_weight = fc_val->weight + 1;
		fc_accum_ndigits = fc_accum_ndigits + (fc_accum_weight - fc_old_weight);
	}

	/*
	 * 即使新值很小，我们可能已用完上一次调用 accum_sum_carry() 时
	 * 为进位数字保留的空间。如果是，扩大以腾出空间给另一个。
	 */
	else if (!fc_accum->have_carry_space)
	{
		fc_accum_weight++;
		fc_accum_ndigits++;
	}

	/* 新值在右侧更宽吗？ */
	fc_accum_rscale = fc_accum_ndigits - fc_accum_weight - 1;
	fc_val_rscale = fc_val->ndigits - fc_val->weight - 1;
	if (fc_val_rscale > fc_accum_rscale)
		fc_accum_ndigits = fc_accum_ndigits + (fc_val_rscale - fc_accum_rscale);

	if (fc_accum_ndigits != fc_old_ndigits ||
		fc_accum_weight != fc_old_weight)
	{
		int32	   *fc_new_pos_digits;
		int32	   *fc_new_neg_digits;
		int			fc_weightdiff;

		fc_weightdiff = fc_accum_weight - fc_old_weight;

		fc_new_pos_digits = palloc0(fc_accum_ndigits * sizeof(int32));
		fc_new_neg_digits = palloc0(fc_accum_ndigits * sizeof(int32));

		if (fc_accum->pos_digits)
		{
			memcpy(&fc_new_pos_digits[fc_weightdiff], fc_accum->pos_digits,
				   fc_old_ndigits * sizeof(int32));
			pfree(fc_accum->pos_digits);

			memcpy(&fc_new_neg_digits[fc_weightdiff], fc_accum->neg_digits,
				   fc_old_ndigits * sizeof(int32));
			pfree(fc_accum->neg_digits);
		}

		fc_accum->pos_digits = fc_new_pos_digits;
		fc_accum->neg_digits = fc_new_neg_digits;

		fc_accum->weight = fc_accum_weight;
		fc_accum->ndigits = fc_accum_ndigits;

		Assert(fc_accum->pos_digits[0] == 0 && fc_accum->neg_digits[0] == 0);
		fc_accum->have_carry_space = true;
	}

	if (fc_val->dscale > fc_accum->dscale)
		fc_accum->dscale = fc_val->dscale;
}

/*
 * 返回累加器的当前值。执行最终的进位传播，并将正和和负和相加。
 *
 * 与所有其他例程不同，调用者不需要切换到持有累加器的内存上下文。
 */
static void fc_accum_sum_final(NumericSumAccum *fc_accum, NumericVar *fc_result)
{
	int			fc_i;
	NumericVar	fc_pos_var;
	NumericVar	fc_neg_var;

	if (fc_accum->ndigits == 0)
	{
		set_var_from_var(&const_zero, fc_result);
		return;
	}

	/* 执行最终进位 */
	fc_accum_sum_carry(fc_accum);

	/* 创建表示正和和负和的 NumericVars */
	init_var(&fc_pos_var);
	init_var(&fc_neg_var);

	fc_pos_var.ndigits = fc_neg_var.ndigits = fc_accum->ndigits;
	fc_pos_var.weight = fc_neg_var.weight = fc_accum->weight;
	fc_pos_var.dscale = fc_neg_var.dscale = fc_accum->dscale;
	fc_pos_var.sign = NUMERIC_POS;
	fc_neg_var.sign = NUMERIC_NEG;

	fc_pos_var.buf = fc_pos_var.digits = digitbuf_alloc(fc_accum->ndigits);
	fc_neg_var.buf = fc_neg_var.digits = digitbuf_alloc(fc_accum->ndigits);

	for (fc_i = 0; fc_i < fc_accum->ndigits; fc_i++)
	{
		Assert(fc_accum->pos_digits[fc_i] < NBASE);
		fc_pos_var.digits[fc_i] = (int16) fc_accum->pos_digits[fc_i];

		Assert(fc_accum->neg_digits[fc_i] < NBASE);
		fc_neg_var.digits[fc_i] = (int16) fc_accum->neg_digits[fc_i];
	}

	/* 并将它们相加 */
	add_var(&fc_pos_var, &fc_neg_var, fc_result);

	/****** 去掉前导/尾随零 ******/
	fc_strip_var(fc_result);
}

/*
 * 复制累加器的状态。
 *
 * 假设 'dst' 事先未初始化。不会尝试释放旧值。
 */
static void fc_accum_sum_copy(NumericSumAccum *fc_dst, NumericSumAccum *fc_src)
{
	fc_dst->pos_digits = palloc(fc_src->ndigits * sizeof(int32));
	fc_dst->neg_digits = palloc(fc_src->ndigits * sizeof(int32));

	memcpy(fc_dst->pos_digits, fc_src->pos_digits, fc_src->ndigits * sizeof(int32));
	memcpy(fc_dst->neg_digits, fc_src->neg_digits, fc_src->ndigits * sizeof(int32));
	fc_dst->num_uncarried = fc_src->num_uncarried;
	fc_dst->ndigits = fc_src->ndigits;
	fc_dst->weight = fc_src->weight;
	fc_dst->dscale = fc_src->dscale;
}

/*
 * 将 'accum2' 的当前值添加到 'accum' 中。
 */
static void fc_accum_sum_combine(NumericSumAccum *fc_accum, NumericSumAccum *fc_accum2)
{
	NumericVar	fc_tmp_var;

	init_var(&fc_tmp_var);

	fc_accum_sum_final(fc_accum2, &fc_tmp_var);
	fc_accum_sum_add(fc_accum, &fc_tmp_var);

	fc_free_var(&fc_tmp_var);
}
